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