Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(555)

Side by Side Diff: chrome/browser/resources/google_now/utility.js

Issue 174053003: Task Management - Handle Promises "Then" or "Catch" callback case. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Promise Tracking 2 Created 6 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 'use strict'; 5 'use strict';
6 6
7 /** 7 /**
8 * @fileoverview Utility objects and functions for Google Now extension. 8 * @fileoverview Utility objects and functions for Google Now extension.
9 * Most important entities here: 9 * Most important entities here:
10 * (1) 'wrapper' is a module used to add error handling and other services to 10 * (1) 'wrapper' is a module used to add error handling and other services to
(...skipping 419 matching lines...) Expand 10 before | Expand all | Expand 10 after
430 })(); 430 })();
431 431
432 wrapper.instrumentChromeApiFunction('alarms.get', 1); 432 wrapper.instrumentChromeApiFunction('alarms.get', 1);
433 wrapper.instrumentChromeApiFunction('alarms.onAlarm.addListener', 0); 433 wrapper.instrumentChromeApiFunction('alarms.onAlarm.addListener', 0);
434 wrapper.instrumentChromeApiFunction('identity.getAuthToken', 1); 434 wrapper.instrumentChromeApiFunction('identity.getAuthToken', 1);
435 wrapper.instrumentChromeApiFunction('identity.onSignInChanged.addListener', 0); 435 wrapper.instrumentChromeApiFunction('identity.onSignInChanged.addListener', 0);
436 wrapper.instrumentChromeApiFunction('identity.removeCachedAuthToken', 1); 436 wrapper.instrumentChromeApiFunction('identity.removeCachedAuthToken', 1);
437 wrapper.instrumentChromeApiFunction('webstorePrivate.getBrowserLogin', 0); 437 wrapper.instrumentChromeApiFunction('webstorePrivate.getBrowserLogin', 0);
438 438
439 /** 439 /**
440 * Add task tracking support to Promise.then. 440 * Promise adapter for all JS promises to the task manager.
441 * @override
442 */ 441 */
443 Promise.prototype.then = function() { 442 function registerPromiseAdapter() {
444 var originalThen = Promise.prototype.then; 443 var originalThen = Promise.prototype.then;
445 return function(callback) { 444 var originalCatch = Promise.prototype.catch;
446 return originalThen.call(this, wrapper.wrapCallback(callback, false)); 445
446 /**
447 * Takes a promise and adds the callback tracker to it.
448 * @param {object} promise Promise that receives the callback tracker.
449 */
450 function instrumentPromise(promise) {
451 if (promise.__tracker === undefined) {
452 promise.__tracker = createPromiseCallbackTracker(promise);
453 }
447 } 454 }
448 }();
449 455
450 /** 456 Promise.prototype.then = function(onResolved, onRejected) {
451 * Add task tracking support to Promise.catch. 457 instrumentPromise(this);
452 * @override 458 return this.__tracker.handleThen(onResolved, onRejected);
453 */
454 Promise.prototype.catch = function() {
455 var originalCatch = Promise.prototype.catch;
456 return function(callback) {
457 return originalCatch.call(this, wrapper.wrapCallback(callback, false));
458 } 459 }
459 }(); 460
461 Promise.prototype.catch = function(onRejected) {
462 instrumentPromise(this);
463 return this.__tracker.handleCatch(onRejected);
464 }
465
466 /**
467 * Promise Callback Tracker.
468 * Handles coordination of 'then' and 'catch' callbacks in a task
469 * manager compatible way. For an individual promise, either the 'then'
470 * arguments or the 'catch' arguemnts will be processed, never both.
471 *
472 * Example:
473 * var p = new Promise([Function]);
474 * p.then([ThenA]);
475 * p.then([ThenB]);
476 * p.catch([CatchA]);
477 * On resolution, [ThenA] and [ThenB] will be used. [CatchA] is discarded.
478 * On rejection, vice versa.
479 *
480 * Clarification:
481 * Chained promises create a new promise that is tracked separately from
482 * the originaing promise, as the example below demonstrates:
483 *
484 * var p = new Promise([Function]));
485 * p.then([ThenA]).then([ThenB]).catch([CatchA]);
486 * ^ ^ ^
487 * | | + Returns a new promise.
488 * | + Returns a new promise.
489 * + Returns a new promise.
490 *
491 * Four promises exist in the above statement, each with its own
492 * resolution and rejection state. However, by default, this state is
493 * chained to the previous previous promise's resolution or rejection
494 * state.
495 *
496 * If p resolves, then the then calls will execute until all the 'then'
497 * clauses are executed. If the result of either [ThenA] or [ThenB] is a
498 * promise, then that execution state will guide the remaining chain.
499 * Similarly, if [CatchA] returns a promise, it can also guide the
500 * remaining chain. In this specific case, the chain ends, so there
501 * is nothing left to do.
502 * @param {object} promise Promise being tracked.
503 * @return {object} A promise callback tracker.
504 */
505 function createPromiseCallbackTracker(promise) {
506 /**
507 * Callback Tracker. Holds an array of callbacks created for this promise.
508 * The indirection allows quick checks against the array and clearing the
509 * array without ugly splicing and copying.
510 * @typedef {{
511 * callback: array.<Function>
512 * }}
513 */
514 var CallbackTracker;
515
516 /** @type {CallbackTracker} */
517 var thenTracker = {callbacks: []};
518 /** @type {CallbackTracker} */
519 var catchTracker = {callbacks: []};
520
521 /**
522 * Returns true if the specified value is callable.
523 * @param {*} value Value to check.
524 * @return {boolean} True if the value is a callable.
525 */
526 function isCallable(value) {
527 return typeof value === 'function';
528 }
529
530 /**
531 * Takes a tracker and clears its callbacks in a manner consistent with
532 * the task manager.
533 * @param {CallbackTracker} tracker Tracker to clear.
534 */
535 function clearTracker(tracker) {
536 if (tracker.callbacks.length > 0) {
537 var callbacksToClear = tracker.callbacks;
538 tracker.callbacks = [];
vadimt 2014/02/25 23:20:22 It's worth adding a comment that this is done to a
robliao 2014/02/25 23:39:54 Done.
539 // Do not wrap the setTimeout callback!
540 // It will call wrapped callbacks.
541 setTimeout(function() {
542 for (var i = 0; i < callbacksToClear.length; i++) {
543 callbacksToClear[i]();
vadimt 2014/02/25 22:36:49 It's not clear why we need to invoke all callbacks
robliao 2014/02/25 22:43:14 The comment says it clears the tracker in a manner
vadimt 2014/02/25 23:20:22 Cool, could you add that in the comment?
robliao 2014/02/25 23:39:54 Added to the banner comment above.
544 }
545 }, 0);
546 }
547 }
548
549 /**
550 * Takes the argument to a 'then' or 'catch' function and applies
551 * a wrapping to callables consistent to ECMA promises.
552 * @param {*} maybeCallback Argument to 'then' or 'catch'.
553 * @param {CallbackTracker} sameTracker Tracker for the call type.
554 * Example: If the argument is from a 'then' call, use thenTracker.
555 * @param {CallbackTracker} otherTracker Tracker for the opposing call type.
556 * Example: If the argument is from a 'then' call, use catchTracker.
557 * @return {*} Consumable argument with necessary wrapping applied.
558 */
559 function registerAndWrapMaybeCallback(
560 maybeCallback, sameTracker, otherTracker) {
vadimt 2014/02/25 22:36:49 maybeCallback can be either function of undefined,
robliao 2014/02/25 22:43:14 maybeCallback is *, as documented above. It can be
561 if (isCallable(maybeCallback)) {
562 var handler = wrapper.wrapCallback(function() {
vadimt 2014/02/25 22:36:49 Are you calling wrapCallback for each then and cat
robliao 2014/02/25 22:43:14 This is why clearTracker calls all callbacks it th
robliao 2014/02/25 22:51:13 Also, calling wrapCallback once would also miscoun
563 if (sameTracker.callbacks.length > 0) {
vadimt 2014/02/25 23:20:22 If you place this check outside of the wrapped par
robliao 2014/02/25 23:39:54 This check must be done in the wrapped part. It is
564 clearTracker(otherTracker);
565 maybeCallback.apply(null, arguments);
566 }
567 }, false);
568 sameTracker.callbacks.push(handler);
569 return handler;
570 } else {
571 return maybeCallback;
572 }
573 }
574
575 /**
576 * Tracks then calls equivalent to Promise.prototype.then.
577 * @param {*} onResolved Argument to use if the promise is resolved.
578 * @param {*} onRejected Argument to use if the promise is rejected.
579 * @return {object} Promise resulting from the 'then' call.
580 */
581 function handleThen(onResolved, onRejected) {
vadimt 2014/02/25 23:20:22 'handle' is not concrete enough. Add? Track? Regis
robliao 2014/02/25 23:39:54 We have precedence for this in Chromium (handleFoc
582 var resolutionHandler =
583 registerAndWrapMaybeCallback(onResolved, thenTracker, catchTracker);
584 var rejectionHandler =
585 registerAndWrapMaybeCallback(onRejected, catchTracker, thenTracker);
586 return originalThen.call(promise, resolutionHandler, rejectionHandler);
587 }
588
589 /**
590 * Tracks then calls equivalent to Promise.prototype.catch.
591 * @param {*} onRejected Argument to use if the promise is rejected.
592 * @return {object} Promise resulting from the 'catch' call.
593 */
594 function handleCatch(onRejected) {
595 var rejectionHandler =
596 registerAndWrapMaybeCallback(onRejected, catchTracker, thenTracker);
597 return originalCatch.call(promise, rejectionHandler);
598 }
599
600 // Seeds this promise with at least one 'then' and 'catch' so that we always
601 // know which set of callbacks will not occur.
602 handleThen(function() {});
603 handleCatch(function() {});
604
605 return {
606 handleThen: handleThen,
607 handleCatch: handleCatch
608 };
609 }
610 }
611
612 registerPromiseAdapter();
460 613
461 /** 614 /**
462 * Builds the object to manage tasks (mutually exclusive chains of events). 615 * Builds the object to manage tasks (mutually exclusive chains of events).
463 * @param {function(string, string): boolean} areConflicting Function that 616 * @param {function(string, string): boolean} areConflicting Function that
464 * checks if a new task can't be added to a task queue that contains an 617 * checks if a new task can't be added to a task queue that contains an
465 * existing task. 618 * existing task.
466 * @return {Object} Task manager interface. 619 * @return {Object} Task manager interface.
467 */ 620 */
468 function buildTaskManager(areConflicting) { 621 function buildTaskManager(areConflicting) {
469 /** 622 /**
(...skipping 353 matching lines...) Expand 10 before | Expand all | Expand 10 after
823 // One hour is just an arbitrary amount of time chosen. 976 // One hour is just an arbitrary amount of time chosen.
824 chrome.alarms.create(alarmName, {periodInMinutes: 60}); 977 chrome.alarms.create(alarmName, {periodInMinutes: 60});
825 978
826 return { 979 return {
827 addListener: addListener, 980 addListener: addListener,
828 getAuthToken: getAuthToken, 981 getAuthToken: getAuthToken,
829 isSignedIn: isSignedIn, 982 isSignedIn: isSignedIn,
830 removeToken: removeToken 983 removeToken: removeToken
831 }; 984 };
832 } 985 }
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698