Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(624)

Unified Diff: src/promise.js

Issue 71713002: Preliminary promise implementation. (Closed) Base URL: https://v8.googlecode.com/svn/branches/bleeding_edge
Patch Set: Created 7 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/messages.js ('k') | test/mjsunit/harmony/iteration-semantics.js » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: src/promise.js
diff --git a/src/promise.js b/src/promise.js
new file mode 100644
index 0000000000000000000000000000000000000000..23801801cd6d3c6726d1661b64592b03dd841100
--- /dev/null
+++ b/src/promise.js
@@ -0,0 +1,271 @@
+// Copyright 2012 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+"use strict";
+
+//This file relies on the fact that the following declaration has been made
+//in runtime.js:
+//var $Object = global.Object;
+//var $WeakMap = global.WeakMap;
+
+
+var $Promise = Promise;
+
+
+// TODO(rossberg): A temporary shim for creating microtasks.
+function Task(task) {
+ var dummy = {}
+ $Object.observe(dummy, task)
+ dummy.dummy = dummy
+}
+
+
+//-------------------------------------------------------------------
+
+// Core functionality.
+
+// Status values: 0 = pending, +1 = resolved, -1 = rejected
+var promiseStatus = NEW_PRIVATE("Promise#status");
+var promiseValue = NEW_PRIVATE("Promise#value");
+var promiseOnResolve = NEW_PRIVATE("Promise#onResolve");
+var promiseOnReject = NEW_PRIVATE("Promise#onReject");
+
+function IsPromise(x) {
+ return IS_SPEC_OBJECT(x) && %HasLocalProperty(x, promiseStatus);
+}
+
+function Promise(resolver) {
+ SET_PRIVATE(this, promiseStatus, 0);
+ SET_PRIVATE(this, promiseOnResolve, []);
+ SET_PRIVATE(this, promiseOnReject, []);
+ var that = this;
+ resolver(function(x) { PromiseResolve(that, x) },
+ function(r) { PromiseReject(that, r) });
+}
+
+function PromiseResolve(promise, x) {
+ if (GET_PRIVATE(promise, promiseStatus) !== 0)
+ throw MakeTypeError('promise_not_pending', [promise]);
+ PromiseQueue(GET_PRIVATE(promise, promiseOnResolve), x);
+ SET_PRIVATE(promise, promiseValue, x);
+ SET_PRIVATE(promise, promiseOnResolve, UNDEFINED);
+ SET_PRIVATE(promise, promiseOnReject, UNDEFINED);
+ SET_PRIVATE(promise, promiseStatus, +1);
+}
+
+function PromiseReject(promise, r) {
+ if (GET_PRIVATE(promise, promiseStatus) !== 0)
+ throw MakeTypeError('promise_not_pending', [promise]);
+ PromiseQueue(GET_PRIVATE(promise, promiseOnReject), r);
+ SET_PRIVATE(promise, promiseValue, r);
+ SET_PRIVATE(promise, promiseOnResolve, UNDEFINED);
+ SET_PRIVATE(promise, promiseOnReject, UNDEFINED);
+ SET_PRIVATE(promise, promiseStatus, -1);
+}
+
+function PromiseQueue(tasks, x) {
+ for (var i in tasks) {
+ Task(function() { tasks[i](x) });
+ }
+}
+
+
+// Convenience.
+
+function PromiseDeferred() {
+ var result = {};
+ result.promise = new this(function(resolve, reject) {
+ result.resolve = resolve;
+ result.reject = reject;
+ })
+ return result;
+}
+
+function PromiseResolved(x) {
+ return new this(function(resolve, reject) { resolve(x) });
+}
+
+function PromiseRejected(r) {
+ return new this(function(resolve, reject) { reject(r) });
+}
+
+
+// Simple chaining (a.k.a. flatMap).
+
+function PromiseNopHandler() {}
+
+function PromiseWhen(onResolve, onReject) {
+ onResolve = IS_UNDEFINED(onResolve) ? PromiseNopHandler : onResolve;
+ onReject = IS_UNDEFINED(onReject) ? PromiseNopHandler : onReject;
+ var deferred = Promise.deferred.call(this.constructor);
+ switch (GET_PRIVATE(this, promiseStatus)) {
+ case UNDEFINED:
+ throw MakeTypeError('not_a_promise', [this]);
+ case 0: // Pending
+ GET_PRIVATE(this, promiseOnResolve).push(
+ PromiseChain(deferred, onResolve));
+ GET_PRIVATE(this, promiseOnReject).push(
+ PromiseChain(deferred, onReject));
+ break
+ case +1: // Resolved
+ PromiseQueue([PromiseChain(deferred, onResolve)],
+ GET_PRIVATE(this, promiseValue));
+ break
+ case -1: // Rejected
+ PromiseQueue([PromiseChain(deferred, onReject)],
+ GET_PRIVATE(this, promiseValue));
+ break
+ default:
+ unreachable();
+ }
+ return deferred.promise;
+}
+
+function PromiseCatch(onReject) {
+ return this.when(UNDEFINED, onReject);
+}
+
+// TODO(rossberg): uncurry
+function PromiseChain(deferred, handler) {
+ return function(x) {
+ try {
+ var y = handler(x);
+ if (y === deferred.promise)
+ throw MakeTypeError('promise_cyclic', [y]);
+ else if (IsPromise(y))
+ y.when(deferred.resolve, deferred.reject);
+ else
+ deferred.resolve(y);
+ } catch(e) {
+ deferred.reject(e);
+ }
+ }
+}
+
+
+// Extended functionality for multi-unwrapping chaining and coercive 'then'.
+
+function PromiseThen(onResolve, onReject) {
+ onResolve = IS_UNDEFINED(onResolve) ? PromiseNopHandler : onResolve
+ var that = this
+ var constructor = this.constructor
+ return this.when(
+ function(x) {
+ x = PromiseCoerce(constructor, x)
+ return x === that ? onReject(MakeTypeError('promise_cyclic', [x])) :
+ IsPromise(x) ? x.then(onResolve, onReject) : onResolve(x)
+ },
+ onReject
+ )
+}
+
+PromiseCoerce.table = new $WeakMap
+
+function PromiseCoerce(constructor, x) {
+ if (IsPromise(x)) {
+ return x
+ } else if (!IS_NULL_OR_UNDEFINED(x) && 'then' in TO_OBJECT(x)) {
+ if (PromiseCoerce.table.has(x)) {
+ return PromiseCoerce.table.get(x)
+ } else {
+ var deferred = constructor.deferred()
+ PromiseCoerce.table.set(x, deferred.promise)
+ try {
+ x.then(deferred.resolve, deferred.reject)
+ } catch(e) {
+ deferred.reject(e)
+ }
+ return deferred.promise
+ }
+ } else {
+ return x
+ }
+}
+
+
+// Combinators.
+
+function PromiseCast(x) {
+ if (x instanceof this) return x
+ if (IsPromise(x)) {
+ var result = this.deferred()
+ x.when(result.resolve, result.reject)
+ return result.promise
+ }
+ return this.resolved(x)
+}
+
+function PromiseAll(values) {
+ var deferred = this.deferred()
+ var count = 0
+ for (var i in values) {
+ ++count
+ this.cast(values[i]).when(
+ function(x) { if (--count === 0) deferred.resolve(undefined) },
+ function(r) { if (count > 0) { count = 0; deferred.reject(r) } }
+ )
+ }
+ return deferred.promise
+}
+
+function PromiseOne(values) {
+ var deferred = this.deferred()
+ var done = false
+ for (var i in values) {
+ this.cast(values[i]).when(
+ function(x) { if (!done) { done = true; deferred.resolve(x) } },
+ function(r) { if (!done) { done = true; deferred.reject(r) } }
+ )
+ }
+ return deferred.promise
+}
+
+//-------------------------------------------------------------------
+
+function SetUpPromise() {
+ %CheckIsBootstrapping()
+
+ global.Promise = $Promise;
+
+ InstallFunctions($Promise, DONT_ENUM, [
+ "deferred", PromiseDeferred,
+ "resolved", PromiseResolved,
+ "rejected", PromiseRejected,
+ "all", PromiseAll,
+ "one", PromiseOne,
+ "cast", PromiseCast
+ ])
+
+ InstallFunctions($Promise.prototype, DONT_ENUM, [
+ "when", PromiseWhen,
+ "then", PromiseThen,
+ "catch", PromiseCatch
+ ])
+}
+
+SetUpPromise();
« no previous file with comments | « src/messages.js ('k') | test/mjsunit/harmony/iteration-semantics.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698