Chromium Code Reviews| Index: chrome/browser/resources/google_now/utility.js |
| diff --git a/chrome/browser/resources/google_now/utility.js b/chrome/browser/resources/google_now/utility.js |
| index f2236209fc498ffa15c0b2cc10c89d254e5a8c84..0a0dc0560dc565789258078a75c1db57cd55a2b6 100644 |
| --- a/chrome/browser/resources/google_now/utility.js |
| +++ b/chrome/browser/resources/google_now/utility.js |
| @@ -437,26 +437,186 @@ wrapper.instrumentChromeApiFunction('identity.removeCachedAuthToken', 1); |
| wrapper.instrumentChromeApiFunction('webstorePrivate.getBrowserLogin', 0); |
| /** |
| - * Add task tracking support to Promise.then. |
| - * @override |
| + * Promise adapter for all JS promises to the task manager. |
| */ |
| -Promise.prototype.then = function() { |
| +function registerPromiseAdapter() { |
| var originalThen = Promise.prototype.then; |
| - return function(callback) { |
| - return originalThen.call(this, wrapper.wrapCallback(callback, false)); |
| + var originalCatch = Promise.prototype.catch; |
| + |
| + /** |
| + * Takes a promise and adds the callback tracker to it. |
| + * @param {object} promise Promise that receives the callback tracker. |
| + */ |
| + function instrumentPromise(promise) { |
| + if (promise.__tracker === undefined) { |
| + promise.__tracker = createPromiseCallbackTracker(promise); |
| + } |
| } |
| -}(); |
| -/** |
| - * Add task tracking support to Promise.catch. |
| - * @override |
| - */ |
| -Promise.prototype.catch = function() { |
| - var originalCatch = Promise.prototype.catch; |
| - return function(callback) { |
| - return originalCatch.call(this, wrapper.wrapCallback(callback, false)); |
| + Promise.prototype.then = function(onResolved, onRejected) { |
| + instrumentPromise(this); |
| + return this.__tracker.handleThen(onResolved, onRejected); |
| } |
| -}(); |
| + |
| + Promise.prototype.catch = function(onRejected) { |
| + instrumentPromise(this); |
| + return this.__tracker.handleCatch(onRejected); |
| + } |
| + |
| + /** |
| + * Promise Callback Tracker. |
| + * Handles coordination of 'then' and 'catch' callbacks in a task |
| + * manager compatible way. For an individual promise, either the 'then' |
| + * arguments or the 'catch' arguemnts will be processed, never both. |
|
rgustafson
2014/02/26 18:31:04
arguments
robliao
2014/02/26 18:46:17
Done.
|
| + * |
| + * Example: |
| + * var p = new Promise([Function]); |
| + * p.then([ThenA]); |
| + * p.then([ThenB]); |
| + * p.catch([CatchA]); |
| + * On resolution, [ThenA] and [ThenB] will be used. [CatchA] is discarded. |
| + * On rejection, vice versa. |
| + * |
| + * Clarification: |
| + * Chained promises create a new promise that is tracked separately from |
| + * the originaing promise, as the example below demonstrates: |
| + * |
| + * var p = new Promise([Function])); |
| + * p.then([ThenA]).then([ThenB]).catch([CatchA]); |
| + * ^ ^ ^ |
| + * | | + Returns a new promise. |
| + * | + Returns a new promise. |
| + * + Returns a new promise. |
| + * |
| + * Four promises exist in the above statement, each with its own |
| + * resolution and rejection state. However, by default, this state is |
| + * chained to the previous previous promise's resolution or rejection |
|
rgustafson
2014/02/26 18:31:04
only one previous
robliao
2014/02/26 18:46:17
Done.
|
| + * state. |
| + * |
| + * If p resolves, then the then calls will execute until all the 'then' |
|
rgustafson
2014/02/26 18:31:04
use "'then' calls" to prevent confusion here
robliao
2014/02/26 18:46:17
Done.
|
| + * clauses are executed. If the result of either [ThenA] or [ThenB] is a |
| + * promise, then that execution state will guide the remaining chain. |
| + * Similarly, if [CatchA] returns a promise, it can also guide the |
| + * remaining chain. In this specific case, the chain ends, so there |
| + * is nothing left to do. |
| + * @param {object} promise Promise being tracked. |
| + * @return {object} A promise callback tracker. |
| + */ |
| + function createPromiseCallbackTracker(promise) { |
| + /** |
| + * Callback Tracker. Holds an array of callbacks created for this promise. |
| + * The indirection allows quick checks against the array and clearing the |
| + * array without ugly splicing and copying. |
| + * @typedef {{ |
| + * callback: array.<Function>= |
| + * }} |
| + */ |
| + var CallbackTracker; |
| + |
| + /** @type {CallbackTracker} */ |
| + var thenTracker = {callbacks: []}; |
| + /** @type {CallbackTracker} */ |
| + var catchTracker = {callbacks: []}; |
| + |
| + /** |
| + * Returns true if the specified value is callable. |
| + * @param {*} value Value to check. |
| + * @return {boolean} True if the value is a callable. |
| + */ |
| + function isCallable(value) { |
| + return typeof value === 'function'; |
| + } |
| + |
| + /** |
| + * Takes a tracker and clears its callbacks in a manner consistent with |
| + * the task manager. For the task manager, it also calls all callbacks |
| + * by no-oping then first and then calling them. |
|
rgustafson
2014/02/26 18:31:04
no-oping them
robliao
2014/02/26 18:46:17
Done.
|
| + * @param {CallbackTracker} tracker Tracker to clear. |
| + */ |
| + function clearTracker(tracker) { |
| + if (tracker.callbacks) { |
| + var callbacksToClear = tracker.callbacks; |
| + // No-ops all callbacks of this type. |
| + tracker.callbacks = undefined; |
| + // Do not wrap the promise then argument! |
| + // It will call wrapped callbacks. |
| + originalThen.call(Promise.resolve(), function() { |
|
vadimt
2014/02/26 01:03:58
WOW!
|
| + for (var i = 0; i < callbacksToClear.length; i++) { |
| + callbacksToClear[i](); |
| + } |
| + }); |
| + } |
| + } |
| + |
| + /** |
| + * Takes the argument to a 'then' or 'catch' function and applies |
| + * a wrapping to callables consistent to ECMA promises. |
| + * @param {*} maybeCallback Argument to 'then' or 'catch'. |
| + * @param {CallbackTracker} sameTracker Tracker for the call type. |
| + * Example: If the argument is from a 'then' call, use thenTracker. |
| + * @param {CallbackTracker} otherTracker Tracker for the opposing call type. |
| + * Example: If the argument is from a 'then' call, use catchTracker. |
| + * @return {*} Consumable argument with necessary wrapping applied. |
| + */ |
| + function registerAndWrapMaybeCallback( |
| + maybeCallback, sameTracker, otherTracker) { |
| + // If sameTracker.callbacks is undefined, we've reached an ending state |
| + // that means this callback will never be called back. |
| + // We will still forward this call on to let the promise system |
| + // handle further processing, but since this promise is in an ending state |
| + // we can be confident it will never be called back. |
| + if (isCallable(maybeCallback) && sameTracker.callbacks) { |
| + var handler = wrapper.wrapCallback(function() { |
| + if (sameTracker.callbacks) { |
|
rgustafson
2014/02/26 18:31:04
This check is already done.
robliao
2014/02/26 18:46:17
This check needs to be checked again since it's in
|
| + clearTracker(otherTracker); |
| + maybeCallback.apply(null, arguments); |
| + } |
| + }, false); |
| + sameTracker.callbacks.push(handler); |
| + return handler; |
| + } else { |
| + return maybeCallback; |
| + } |
| + } |
| + |
| + /** |
| + * Tracks then calls equivalent to Promise.prototype.then. |
| + * @param {*} onResolved Argument to use if the promise is resolved. |
| + * @param {*} onRejected Argument to use if the promise is rejected. |
| + * @return {object} Promise resulting from the 'then' call. |
| + */ |
| + function handleThen(onResolved, onRejected) { |
| + var resolutionHandler = |
| + registerAndWrapMaybeCallback(onResolved, thenTracker, catchTracker); |
| + var rejectionHandler = |
| + registerAndWrapMaybeCallback(onRejected, catchTracker, thenTracker); |
| + return originalThen.call(promise, resolutionHandler, rejectionHandler); |
| + } |
| + |
| + /** |
| + * Tracks then calls equivalent to Promise.prototype.catch. |
| + * @param {*} onRejected Argument to use if the promise is rejected. |
| + * @return {object} Promise resulting from the 'catch' call. |
| + */ |
| + function handleCatch(onRejected) { |
| + var rejectionHandler = |
| + registerAndWrapMaybeCallback(onRejected, catchTracker, thenTracker); |
| + return originalCatch.call(promise, rejectionHandler); |
| + } |
| + |
| + // Seeds this promise with at least one 'then' and 'catch' so that we always |
| + // know which set of callbacks will not occur. |
|
rgustafson
2014/02/26 18:31:04
I don't understand what this is saying at all. Cha
robliao
2014/02/26 18:46:17
Done.
|
| + handleThen(function() {}); |
| + handleCatch(function() {}); |
| + |
| + return { |
| + handleThen: handleThen, |
| + handleCatch: handleCatch |
| + }; |
| + } |
| +} |
| + |
| +registerPromiseAdapter(); |
| /** |
| * Builds the object to manage tasks (mutually exclusive chains of events). |