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

Unified Diff: src/promise.js

Issue 64223010: Harmony promises (Closed) Base URL: https://v8.googlecode.com/svn/branches/bleeding_edge
Patch Set: "Renamed .when to .chain; Dmitry's comments" 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/objects.cc ('k') | src/runtime.h » ('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..30f4f07b4b7b21c1c83b0c6e590eeb8f4c2803d6
--- /dev/null
+++ b/src/promise.js
@@ -0,0 +1,305 @@
+// 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;
+
+
+//-------------------------------------------------------------------
+
+// Core functionality.
+
+// Event queue format: [(value, [(handler, deferred)*])*]
+// I.e., a list of value/tasks pairs, where the value is a resolution value or
+// rejection reason, and the tasks are a respective list of handler/deferred
+// pairs waiting for notification of this value. Each handler is an onResolve or
+// onReject function provided to the same call of 'chain' that produced the
+// associated deferred.
+var promiseEvents = new InternalArray;
+
+// 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");
+var promiseRaw = NEW_PRIVATE("Promise#raw");
+
+function IsPromise(x) {
+ return IS_SPEC_OBJECT(x) && %HasLocalProperty(x, promiseStatus);
+}
+
+function Promise(resolver) {
+ if (resolver === promiseRaw) return;
+ var promise = PromiseInit(this);
+ resolver(function(x) { PromiseResolve(promise, x) },
+ function(r) { PromiseReject(promise, r) });
+ // TODO(rossberg): current draft makes exception from this call asynchronous,
+ // but that's probably a mistake.
+}
+
+function PromiseSet(promise, status, value, onResolve, onReject) {
+ SET_PRIVATE(promise, promiseStatus, status);
+ SET_PRIVATE(promise, promiseValue, value);
+ SET_PRIVATE(promise, promiseOnResolve, onResolve);
+ SET_PRIVATE(promise, promiseOnReject, onReject);
+ return promise;
+}
+
+function PromiseInit(promise) {
+ return PromiseSet(promise, 0, UNDEFINED, new InternalArray, new InternalArray)
+}
+
+function PromiseDone(promise, status, value, promiseQueue) {
+ if (GET_PRIVATE(promise, promiseStatus) !== 0) return;
+ PromiseEnqueue(value, GET_PRIVATE(promise, promiseQueue));
+ PromiseSet(promise, status, value);
+}
+
+function PromiseResolve(promise, x) {
+ PromiseDone(promise, +1, x, promiseOnResolve)
+}
+
+function PromiseReject(promise, r) {
+ PromiseDone(promise, -1, r, promiseOnReject)
+}
+
+
+// Convenience.
+
+function PromiseDeferred() {
+ if (this === $Promise) {
+ // Optimized case, avoid extra closure.
+ var promise = PromiseInit(new Promise(promiseRaw));
+ return {
+ promise: promise,
+ resolve: function(x) { PromiseResolve(promise, x) },
+ reject: function(r) { PromiseReject(promise, r) }
+ };
+ } else {
+ var result = {};
+ result.promise = new this(function(resolve, reject) {
+ result.resolve = resolve;
+ result.reject = reject;
+ })
+ return result;
+ }
+}
+
+function PromiseResolved(x) {
+ if (this === $Promise) {
+ // Optimized case, avoid extra closure.
+ return PromiseSet(new Promise(promiseRaw), +1, x);
+ } else {
+ return new this(function(resolve, reject) { resolve(x) });
+ }
+}
+
+function PromiseRejected(r) {
+ if (this === $Promise) {
+ // Optimized case, avoid extra closure.
+ return PromiseSet(new Promise(promiseRaw), -1, r);
+ } else {
+ return new this(function(resolve, reject) { reject(r) });
+ }
+}
+
+
+// Simple chaining.
+
+function PromiseIdResolveHandler(x) { return x }
+function PromiseIdRejectHandler(r) { throw r }
+
+function PromiseChain(onResolve, onReject) { // a.k.a. flatMap
+ onResolve = IS_UNDEFINED(onResolve) ? PromiseIdResolveHandler : onResolve;
+ onReject = IS_UNDEFINED(onReject) ? PromiseIdRejectHandler : onReject;
+ var deferred = %_CallFunction(this.constructor, PromiseDeferred);
+ switch (GET_PRIVATE(this, promiseStatus)) {
+ case UNDEFINED:
+ throw MakeTypeError('not_a_promise', [this]);
+ case 0: // Pending
+ GET_PRIVATE(this, promiseOnResolve).push(onResolve, deferred);
+ GET_PRIVATE(this, promiseOnReject).push(onReject, deferred);
+ break;
+ case +1: // Resolved
+ PromiseEnqueue(GET_PRIVATE(this, promiseValue), [onResolve, deferred]);
+ break;
+ case -1: // Rejected
+ PromiseEnqueue(GET_PRIVATE(this, promiseValue), [onReject, deferred]);
+ break;
+ }
+ return deferred.promise;
+}
+
+function PromiseCatch(onReject) {
+ return this.chain(UNDEFINED, onReject);
+}
+
+function PromiseEnqueue(value, tasks) {
+ promiseEvents.push(value, tasks);
+ %SetMicrotaskPending(true);
+}
+
+function PromiseMicrotaskRunner() {
+ var events = promiseEvents;
+ if (events.length > 0) {
+ promiseEvents = new InternalArray;
+ for (var i = 0; i < events.length; i += 2) {
+ var value = events[i];
+ var tasks = events[i + 1];
+ for (var j = 0; j < tasks.length; j += 2) {
+ var handler = tasks[j];
+ var deferred = tasks[j + 1];
+ try {
+ var result = handler(value);
+ if (result === deferred.promise)
+ throw MakeTypeError('promise_cyclic', [result]);
+ else if (IsPromise(result))
+ result.chain(deferred.resolve, deferred.reject);
+ else
+ deferred.resolve(result);
+ } catch(e) {
+ // TODO(rossberg): perhaps log uncaught exceptions below.
+ try { deferred.reject(e) } catch(e) {}
+ }
+ }
+ }
+ }
+}
+RunMicrotasks.runners.push(PromiseMicrotaskRunner);
+
+
+// Multi-unwrapped chaining with thenable coercion.
+
+function PromiseThen(onResolve, onReject) {
+ onResolve = IS_UNDEFINED(onResolve) ? PromiseIdResolveHandler : onResolve;
+ var that = this;
+ var constructor = this.constructor;
+ return this.chain(
+ 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) {
+ var then;
+ if (IsPromise(x)) {
+ return x;
+ } else if (!IS_NULL_OR_UNDEFINED(x) && %IsCallable(then = x.then)) {
+ if (PromiseCoerce.table.has(x)) {
+ return PromiseCoerce.table.get(x);
+ } else {
+ var deferred = constructor.deferred();
+ PromiseCoerce.table.set(x, deferred.promise);
+ try {
+ %_CallFunction(x, deferred.resolve, deferred.reject, then);
+ } catch(e) {
+ deferred.reject(e);
+ }
+ return deferred.promise;
+ }
+ } else {
+ return x;
+ }
+}
+
+
+// Combinators.
+
+function PromiseCast(x) {
+ // TODO(rossberg): cannot do better until we support @@create.
+ return IsPromise(x) ? x : this.resolved(x);
+}
+
+function PromiseAll(values) {
+ var deferred = this.deferred();
+ var resolutions = [];
+ var count = values.length;
+ if (count === 0) {
+ deferred.resolve(resolutions);
+ } else {
+ for (var i = 0; i < values.length; ++i) {
+ this.cast(values[i]).chain(
+ function(i, x) {
+ resolutions[i] = x;
+ if (--count === 0) deferred.resolve(resolutions);
+ }.bind(UNDEFINED, i), // TODO(rossberg): use let loop once available
+ function(r) {
+ if (count > 0) { count = 0; deferred.reject(r) }
+ }
+ );
+ }
+ }
+ return deferred.promise;
+}
+
+function PromiseOne(values) { // a.k.a. race
+ var deferred = this.deferred();
+ var done = false;
+ for (var i = 0; i < values.length; ++i) {
+ this.cast(values[i]).chain(
+ 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, [
+ "chain", PromiseChain,
+ "then", PromiseThen,
+ "catch", PromiseCatch
+ ]);
+}
+
+SetUpPromise();
« no previous file with comments | « src/objects.cc ('k') | src/runtime.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698