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