summaryrefslogtreecommitdiff
path: root/chromium/ui/webui/resources/js/cr/promise.js
blob: f0681c123c99662db983e093bb8bc1382850c84f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

/**
 * @fileoverview This implementes a future promise class.
 */

cr.define('cr', function() {

  /**
   * Sentinel used to mark a value as pending.
   * @const
   */
  var PENDING_VALUE = {};

  /**
   * Creates a future promise.
   * @param {*=} opt_value The value to set the promise to. If set completes
   *     the promise immediately.
   * @constructor
   */
  function Promise(opt_value) {
    /**
     * An array of the callbacks.
     * @type {!Array.<!Function>}
     * @private
     */
    this.callbacks_ = [];

    if (arguments.length > 0)
      this.value = opt_value;
  }

  Promise.prototype = {
    /**
     * The current value.
     * @type {*}
     * @private
     */
    value_: PENDING_VALUE,

    /**
     * The value of the future promise. Accessing this before the promise has
     * been fulfilled will throw an error. If this is set to an exception
     * accessing this will throw as well.
     * @type {*}
     */
    get value() {
      return this.done ? this.value_ : undefined;
    },
    set value(value) {
      if (!this.done) {
        this.value_ = value;
        for (var i = 0; i < this.callbacks_.length; i++) {
          this.callbacks_[i].call(null, value);
        }
        this.callbacks_.length = 0;
      }
    },

    /**
     * Whether the future promise has been fulfilled.
     * @type {boolean}
     */
    get done() {
      return this.value_ !== PENDING_VALUE;
    },

    /**
     * Adds a listener to the future promise. The function will be called when
     * the promise is fulfilled. If the promise is already fullfilled this will
     * never call the function.
     * @param {!Function} fun The function to call.
     */
    addListener: function(fun) {
      if (this.done)
        fun(this.value);
      else
        this.callbacks_.push(fun);
    },

    /**
     * Removes a previously added listener from the future promise.
     * @param {!Function} fun The function to remove.
     */
    removeListener: function(fun) {
      var i = this.callbacks_.indexOf(fun);
      if (i >= 0)
        this.callbacks_.splice(i, 1);
    },

    /**
     * If the promise is done then this returns the string representation of
     * the value.
     * @return {string} The string representation of the promise.
     * @override
     */
    toString: function() {
      if (this.done)
        return String(this.value);
      else
        return '[object Promise]';
    },

    /**
     * Override to allow arithmetic.
     * @override
     */
    valueOf: function() {
      return this.value;
    }
  };

  /**
   * When a future promise is done call {@code fun}. This also calls the
   * function if the promise has already been fulfilled.
   * @param {!Promise} p The promise.
   * @param {!Function} fun The function to call when the promise is fulfilled.
   */
  Promise.when = function(p, fun) {
    p.addListener(fun);
  };

  /**
   * Creates a new promise the will be fulfilled after {@code t} ms.
   * @param {number} t The time to wait before the promise is fulfilled.
   * @param {*=} opt_value The value to return after the wait.
   * @return {!Promise} The new future promise.
   */
  Promise.wait = function(t, opt_value) {
    var p = new Promise;
    window.setTimeout(function() {
      p.value = opt_value;
    }, t);
    return p;
  };

  /**
   * Creates a new future promise that is fulfilled when any of the promises are
   * fulfilled. The value of the returned promise will be the value of the first
   * fulfilled promise.
   * @param {...!Promise} var_args The promises used to build up the new
   *     promise.
   * @return {!Promise} The new promise that will be fulfilled when any of the
   *     passed in promises are fulfilled.
   */
  Promise.any = function(var_args) {
    var p = new Promise;
    function f(v) {
      p.value = v;
    }
    for (var i = 0; i < arguments.length; i++) {
      arguments[i].addListener(f);
    }
    return p;
  };

  /**
   * Creates a new future promise that is fulfilled when all of the promises are
   * fulfilled. The value of the returned promise is an array of the values of
   * the promises passed in.
   * @param {...!Promise} var_args The promises used to build up the new
   *     promise.
   * @return {!Promise} The promise that wraps all the promises in the array.
   */
  Promise.all = function(var_args) {
    var p = new Promise;
    var args = Array.prototype.slice.call(arguments);
    var count = args.length;
    if (!count) {
      p.value = [];
      return p;
    }

    function f(v) {
      count--;
      if (!count) {
        p.value = args.map(function(argP) {
          return argP.value;
        });
      }
    }

    // Do not use count here since count may be decremented in the call to
    // addListener if the promise is already done.
    for (var i = 0; i < args.length; i++) {
      args[i].addListener(f);
    }

    return p;
  };

  /**
   * Wraps an event in a future promise.
   * @param {!EventTarget} target The object that dispatches the event.
   * @param {string} type The type of the event.
   * @param {boolean=} opt_useCapture Whether to listen to the capture phase or
   *     the bubble phase.
   * @return {!Promise} The promise that will be fulfilled when the event is
   *     dispatched.
   */
  Promise.event = function(target, type, opt_useCapture) {
    var p = new Promise;
    target.addEventListener(type, function(e) {
      p.value = e;
    }, opt_useCapture);
    return p;
  };

  return {
    Promise: Promise
  };
});