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

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: Modify Comments 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. For the task manager, it also calls all callbacks
533 * by no-oping then first and then calling them.
534 * @param {CallbackTracker} tracker Tracker to clear.
535 */
536 function clearTracker(tracker) {
537 if (tracker.callbacks.length > 0) {
538 var callbacksToClear = tracker.callbacks;
539 // No-ops all callbacks of this type.
540 tracker.callbacks = [];
541 // Do not wrap the setTimeout callback!
542 // It will call wrapped callbacks.
543 setTimeout(function() {
vadimt 2014/02/26 00:31:46 Please make sure setTimeout keeps event page loade
robliao 2014/02/26 00:58:36 Cute trick with the promise. On 2014/02/26 00:31:4
544 for (var i = 0; i < callbacksToClear.length; i++) {
545 callbacksToClear[i]();
546 }
547 }, 0);
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 (isCallable(maybeCallback)) {
564 var handler = wrapper.wrapCallback(function() {
565 if (sameTracker.callbacks.length > 0) {
566 clearTracker(otherTracker);
567 maybeCallback.apply(null, arguments);
568 }
569 }, false);
570 sameTracker.callbacks.push(handler);
571 return handler;
572 } else {
573 return maybeCallback;
574 }
575 }
576
577 /**
578 * Tracks then calls equivalent to Promise.prototype.then.
579 * @param {*} onResolved Argument to use if the promise is resolved.
580 * @param {*} onRejected Argument to use if the promise is rejected.
581 * @return {object} Promise resulting from the 'then' call.
582 */
583 function handleThen(onResolved, onRejected) {
584 var resolutionHandler =
585 registerAndWrapMaybeCallback(onResolved, thenTracker, catchTracker);
586 var rejectionHandler =
587 registerAndWrapMaybeCallback(onRejected, catchTracker, thenTracker);
588 return originalThen.call(promise, resolutionHandler, rejectionHandler);
589 }
590
591 /**
592 * Tracks then calls equivalent to Promise.prototype.catch.
593 * @param {*} onRejected Argument to use if the promise is rejected.
594 * @return {object} Promise resulting from the 'catch' call.
595 */
596 function handleCatch(onRejected) {
597 var rejectionHandler =
598 registerAndWrapMaybeCallback(onRejected, catchTracker, thenTracker);
599 return originalCatch.call(promise, rejectionHandler);
600 }
601
602 // Seeds this promise with at least one 'then' and 'catch' so that we always
603 // know which set of callbacks will not occur.
604 handleThen(function() {});
605 handleCatch(function() {});
606
607 return {
608 handleThen: handleThen,
609 handleCatch: handleCatch
610 };
611 }
612 }
613
614 registerPromiseAdapter();
460 615
461 /** 616 /**
462 * Builds the object to manage tasks (mutually exclusive chains of events). 617 * Builds the object to manage tasks (mutually exclusive chains of events).
463 * @param {function(string, string): boolean} areConflicting Function that 618 * @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 619 * checks if a new task can't be added to a task queue that contains an
465 * existing task. 620 * existing task.
466 * @return {Object} Task manager interface. 621 * @return {Object} Task manager interface.
467 */ 622 */
468 function buildTaskManager(areConflicting) { 623 function buildTaskManager(areConflicting) {
469 /** 624 /**
(...skipping 353 matching lines...) Expand 10 before | Expand all | Expand 10 after
823 // One hour is just an arbitrary amount of time chosen. 978 // One hour is just an arbitrary amount of time chosen.
824 chrome.alarms.create(alarmName, {periodInMinutes: 60}); 979 chrome.alarms.create(alarmName, {periodInMinutes: 60});
825 980
826 return { 981 return {
827 addListener: addListener, 982 addListener: addListener,
828 getAuthToken: getAuthToken, 983 getAuthToken: getAuthToken,
829 isSignedIn: isSignedIn, 984 isSignedIn: isSignedIn,
830 removeToken: removeToken 985 removeToken: removeToken
831 }; 986 };
832 } 987 }
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