• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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