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