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

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: Theoretical Bug Fix and CR Feedback Created 6 years, 10 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.
rgustafson 2014/02/26 18:31:04 arguments
robliao 2014/02/26 18:46:17 Done.
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
rgustafson 2014/02/26 18:31:04 only one previous
robliao 2014/02/26 18:46:17 Done.
494 * state.
495 *
496 * 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.
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. For the task manager, it also calls all callbacks
533 * 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.
534 * @param {CallbackTracker} tracker Tracker to clear.
535 */
536 function clearTracker(tracker) {
537 if (tracker.callbacks) {
538 var callbacksToClear = tracker.callbacks;
539 // No-ops all callbacks of this type.
540 tracker.callbacks = undefined;
541 // Do not wrap the promise then argument!
542 // It will call wrapped callbacks.
543 originalThen.call(Promise.resolve(), function() {
vadimt 2014/02/26 01:03:58 WOW!
544 for (var i = 0; i < callbacksToClear.length; i++) {
545 callbacksToClear[i]();
546 }
547 });
548 }
549 }
550
551 /**
552 * Takes the argument to a 'then' or 'catch' function and applies
553 * a wrapping to callables consistent to ECMA promises.
554 * @param {*} maybeCallback Argument to 'then' or 'catch'.
555 * @param {CallbackTracker} sameTracker Tracker for the call type.
556 * Example: If the argument is from a 'then' call, use thenTracker.
557 * @param {CallbackTracker} otherTracker Tracker for the opposing call type.
558 * Example: If the argument is from a 'then' call, use catchTracker.
559 * @return {*} Consumable argument with necessary wrapping applied.
560 */
561 function registerAndWrapMaybeCallback(
562 maybeCallback, sameTracker, otherTracker) {
563 // If sameTracker.callbacks is undefined, we've reached an ending state
564 // that means this callback will never be called back.
565 // We will still forward this call on to let the promise system
566 // handle further processing, but since this promise is in an ending state
567 // we can be confident it will never be called back.
568 if (isCallable(maybeCallback) && sameTracker.callbacks) {
569 var handler = wrapper.wrapCallback(function() {
570 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
571 clearTracker(otherTracker);
572 maybeCallback.apply(null, arguments);
573 }
574 }, false);
575 sameTracker.callbacks.push(handler);
576 return handler;
577 } else {
578 return maybeCallback;
579 }
580 }
581
582 /**
583 * Tracks then calls equivalent to Promise.prototype.then.
584 * @param {*} onResolved Argument to use if the promise is resolved.
585 * @param {*} onRejected Argument to use if the promise is rejected.
586 * @return {object} Promise resulting from the 'then' call.
587 */
588 function handleThen(onResolved, onRejected) {
589 var resolutionHandler =
590 registerAndWrapMaybeCallback(onResolved, thenTracker, catchTracker);
591 var rejectionHandler =
592 registerAndWrapMaybeCallback(onRejected, catchTracker, thenTracker);
593 return originalThen.call(promise, resolutionHandler, rejectionHandler);
594 }
595
596 /**
597 * Tracks then calls equivalent to Promise.prototype.catch.
598 * @param {*} onRejected Argument to use if the promise is rejected.
599 * @return {object} Promise resulting from the 'catch' call.
600 */
601 function handleCatch(onRejected) {
602 var rejectionHandler =
603 registerAndWrapMaybeCallback(onRejected, catchTracker, thenTracker);
604 return originalCatch.call(promise, rejectionHandler);
605 }
606
607 // Seeds this promise with at least one 'then' and 'catch' so that we always
608 // 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.
609 handleThen(function() {});
610 handleCatch(function() {});
611
612 return {
613 handleThen: handleThen,
614 handleCatch: handleCatch
615 };
616 }
617 }
618
619 registerPromiseAdapter();
460 620
461 /** 621 /**
462 * Builds the object to manage tasks (mutually exclusive chains of events). 622 * Builds the object to manage tasks (mutually exclusive chains of events).
463 * @param {function(string, string): boolean} areConflicting Function that 623 * @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 624 * checks if a new task can't be added to a task queue that contains an
465 * existing task. 625 * existing task.
466 * @return {Object} Task manager interface. 626 * @return {Object} Task manager interface.
467 */ 627 */
468 function buildTaskManager(areConflicting) { 628 function buildTaskManager(areConflicting) {
469 /** 629 /**
(...skipping 353 matching lines...) Expand 10 before | Expand all | Expand 10 after
823 // One hour is just an arbitrary amount of time chosen. 983 // One hour is just an arbitrary amount of time chosen.
824 chrome.alarms.create(alarmName, {periodInMinutes: 60}); 984 chrome.alarms.create(alarmName, {periodInMinutes: 60});
825 985
826 return { 986 return {
827 addListener: addListener, 987 addListener: addListener,
828 getAuthToken: getAuthToken, 988 getAuthToken: getAuthToken,
829 isSignedIn: isSignedIn, 989 isSignedIn: isSignedIn,
830 removeToken: removeToken 990 removeToken: removeToken
831 }; 991 };
832 } 992 }
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