1c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Copyright (c) 2010 The Chromium Authors. All rights reserved. 2c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Use of this source code is governed by a BSD-style license that can be 3c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// found in the LICENSE file. 4c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 5c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch/** 6c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch * @fileoverview This implementes a future promise class. 7c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch */ 8c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 9c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochcr.define('cr', function() { 10c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 11c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch /** 12c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch * Sentinel used to mark a value as pending. 13c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch */ 14c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const PENDING_VALUE = {}; 15c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 16c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch /** 17c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch * Creates a future promise. 18c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch * @param {*=} opt_value The value to set the promise to. If set completes 19c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch * the promise immediately. 20c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch * @constructor 21c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch */ 22c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch function Promise(opt_value) { 23c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch /** 24c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch * An array of the callbacks. 25c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch * @type {!Array.<!Function>} 26c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch * @private 27c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch */ 28c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch this.callbacks_ = []; 29c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 30c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (arguments.length > 0) 31c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch this.value = opt_value; 32c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 33c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 34c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch Promise.prototype = { 35c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch /** 36c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch * The current value. 37c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch * @type {*} 38c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch * @private 39c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch */ 40c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch value_: PENDING_VALUE, 41c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 42c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch /** 43c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch * The value of the future promise. Accessing this before the promise has 44c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch * been fulfilled will throw an error. If this is set to an exception 45c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch * accessing this will throw as well. 46c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch * @type {*} 47c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch */ 48c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch get value() { 49c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return this.done ? this.value_ : undefined; 50c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch }, 51c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch set value(value) { 52c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!this.done) { 53c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch this.value_ = value; 54c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch for (var i = 0; i < this.callbacks_.length; i++) { 55c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch this.callbacks_[i].call(null, value); 56c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 57c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch this.callbacks_.length = 0; 58c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 59c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch }, 60c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 61c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch /** 62c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch * Whether the future promise has been fulfilled. 63c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch * @type {boolean} 64c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch */ 65c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch get done() { 66c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return this.value_ !== PENDING_VALUE; 67c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch }, 68c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 69c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch /** 70c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch * Adds a listener to the future promise. The function will be called when 71c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch * the promise is fulfilled. If the promise is already fullfilled this will 72c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch * never call the function. 73c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch * @param {!Function} fun The function to call. 74c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch */ 75c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch addListener: function(fun) { 76c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (this.done) 77c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch fun(this.value); 78c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch else 79c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch this.callbacks_.push(fun); 80c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch }, 81c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 82c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch /** 83c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch * Removes a previously added listener from the future promise. 84c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch * @param {!Function} fun The function to remove. 85c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch */ 86c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch removeListener: function(fun) { 87c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch var i = this.callbacks_.indexOf(fun); 88c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (i >= 0) 89c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch this.callbacks_.splice(i, 1); 90c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch }, 91c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 92c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch /** 93c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch * If the promise is done then this returns the string representation of 94c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch * the value. 95c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch * @return {string} The string representation of the promise. 96c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch * @override 97c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch */ 98c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch toString: function() { 99c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (this.done) 100c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return String(this.value); 101c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch else 102c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return '[object Promise]'; 103c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch }, 104c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 105c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch /** 106c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch * Override to allow arithmetic. 107c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch * @override 108c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch */ 109c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch valueOf: function() { 110c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return this.value; 111c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 112c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch }; 113c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 114c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch /** 115c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch * When a future promise is done call {@code fun}. This also calls the 116c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch * function if the promise has already been fulfilled. 117c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch * @param {!Promise} p The promise. 118c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch * @param {!Function} fun The function to call when the promise is fulfilled. 119c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch */ 120c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch Promise.when = function(p, fun) { 121c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch p.addListener(fun); 122c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch }; 123c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 124c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch /** 125c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch * Creates a new promise the will be fulfilled after {@code t} ms. 126c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch * @param {number} t The time to wait before the promise is fulfilled. 127c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch * @param {*=} opt_value The value to return after the wait. 128c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch * @return {!Promise} The new future promise. 129c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch */ 130c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch Promise.wait = function(t, opt_value) { 131c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch var p = new Promise; 132c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch window.setTimeout(function() { 133c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch p.value = opt_value; 134c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch }, t); 135c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return p; 136c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch }; 137c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 138c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch /** 139c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch * Creates a new future promise that is fulfilled when any of the promises are 140c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch * fulfilled. The value of the returned promise will be the value of the first 141c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch * fulfilled promise. 142c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch * @param {...!Promise} var_args The promises used to build up the new 143c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch * promise. 144c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch * @return {!Promise} The new promise that will be fulfilled when any of the 145c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch * passed in promises are fulfilled. 146c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch */ 147c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch Promise.any = function(var_args) { 148c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch var p = new Promise; 149c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch function f(v) { 150c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch p.value = v; 151c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 152c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch for (var i = 0; i < arguments.length; i++) { 153c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch arguments[i].addListener(f); 154c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 155c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return p; 156c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch }; 157c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 158c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch /** 159c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch * Creates a new future promise that is fulfilled when all of the promises are 160c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch * fulfilled. The value of the returned promise is an array of the values of 161c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch * the promises passed in. 162c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch * @param {...!Promise} var_args The promises used to build up the new 163c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch * promise. 164c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch * @return {!Promise} The promise that wraps all the promises in the array. 165c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch */ 166c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch Promise.all = function(var_args) { 167c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch var p = new Promise; 168c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch var args = Array.prototype.slice.call(arguments); 169c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch var count = args.length; 170c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!count) { 171c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch p.value = []; 172c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return p; 173c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 174c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 175c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch function f(v) { 176c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch count--; 177c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!count) { 178c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch p.value = args.map(function(argP) { 179c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return argP.value; 180c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch }); 181c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 182c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 183c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 184c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Do not use count here since count may be decremented in the call to 185c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // addListener if the promise is already done. 186c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch for (var i = 0; i < args.length; i++) { 187c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch args[i].addListener(f); 188c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 189c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 190c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return p; 191c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch }; 192c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 193c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch /** 194c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch * Wraps an event in a future promise. 195c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch * @param {!EventTarget} target The object that dispatches the event. 196c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch * @param {string} type The type of the event. 197c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch * @param {boolean=} opt_useCapture Whether to listen to the capture phase or 198c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch * the bubble phase. 199c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch * @return {!Promise} The promise that will be fulfilled when the event is 200c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch * dispatched. 201c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch */ 202c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch Promise.event = function(target, type, opt_useCapture) { 203c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch var p = new Promise; 204c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch target.addEventListener(type, function(e) { 205c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch p.value = e; 206c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch }, opt_useCapture); 207c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return p; 208c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch }; 209c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 210c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return { 211c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch Promise: Promise 212c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch }; 213c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}); 214