Index: src/js/promise.js |
diff --git a/src/js/promise.js b/src/js/promise.js |
index ad211b8972acad747226a7fa94c39270d74f9880..fcb0826f8405c1839ec1f3cafda887b18de9294b 100644 |
--- a/src/js/promise.js |
+++ b/src/js/promise.js |
@@ -21,6 +21,8 @@ var promiseRejectReactionsSymbol = |
utils.ImportNow("promise_reject_reactions_symbol"); |
var promiseFulfillReactionsSymbol = |
utils.ImportNow("promise_fulfill_reactions_symbol"); |
+var promiseDeferredReactionsSymbol = |
+ utils.ImportNow("promise_deferred_reactions_symbol"); |
var promiseRawSymbol = utils.ImportNow("promise_raw_symbol"); |
var promiseStateSymbol = utils.ImportNow("promise_state_symbol"); |
var promiseResultSymbol = utils.ImportNow("promise_result_symbol"); |
@@ -98,11 +100,22 @@ var GlobalPromise = function Promise(executor) { |
// Core functionality. |
-function PromiseSet(promise, status, value, onResolve, onReject) { |
+function PromiseSet(promise, status, value) { |
SET_PRIVATE(promise, promiseStateSymbol, status); |
SET_PRIVATE(promise, promiseResultSymbol, value); |
- SET_PRIVATE(promise, promiseFulfillReactionsSymbol, onResolve); |
- SET_PRIVATE(promise, promiseRejectReactionsSymbol, onReject); |
+ |
+ // There are 3 possible states for the resolve, reject symbols when we add |
+ // a new callback -- |
+ // 1) UNDEFINED -- This is the zero state where there is no callback |
+ // registered. When we see this state, we directly attach the callbacks to |
+ // the symbol. |
+ // 2) !IS_ARRAY -- There is a single callback directly attached to the |
+ // symbols. We need to create a new array to store additional callbacks. |
+ // 3) IS_ARRAY -- There are multiple callbacks already registered, |
+ // therefore we can just push the new callback to the existing array. |
+ SET_PRIVATE(promise, promiseFulfillReactionsSymbol, UNDEFINED); |
+ SET_PRIVATE(promise, promiseRejectReactionsSymbol, UNDEFINED); |
+ SET_PRIVATE(promise, promiseDeferredReactionsSymbol, UNDEFINED); |
return promise; |
} |
@@ -115,13 +128,17 @@ function PromiseCreateAndSet(status, value) { |
function PromiseInit(promise) { |
return PromiseSet( |
- promise, kPending, UNDEFINED, new InternalArray, new InternalArray) |
+ promise, kPending, UNDEFINED, UNDEFINED, UNDEFINED, UNDEFINED); |
} |
function FulfillPromise(promise, status, value, promiseQueue) { |
if (GET_PRIVATE(promise, promiseStateSymbol) === kPending) { |
var tasks = GET_PRIVATE(promise, promiseQueue); |
- if (tasks.length) PromiseEnqueue(value, tasks, status); |
+ if (!IS_UNDEFINED(tasks)) { |
+ var tasks = GET_PRIVATE(promise, promiseQueue); |
+ var deferreds = GET_PRIVATE(promise, promiseDeferredReactionsSymbol); |
+ PromiseEnqueue(value, tasks, deferreds, status); |
+ } |
PromiseSet(promise, status, value); |
} |
} |
@@ -139,14 +156,18 @@ function PromiseHandle(value, handler, deferred) { |
} |
} |
-function PromiseEnqueue(value, tasks, status) { |
+function PromiseEnqueue(value, tasks, deferreds, status) { |
var id, name, instrumenting = DEBUG_IS_ACTIVE; |
%EnqueueMicrotask(function() { |
if (instrumenting) { |
%DebugAsyncTaskEvent({ type: "willHandle", id: id, name: name }); |
} |
- for (var i = 0; i < tasks.length; i += 2) { |
- PromiseHandle(value, tasks[i], tasks[i + 1]) |
+ if (IS_ARRAY(tasks)) { |
+ for (var i = 0; i < tasks.length; i += 1) { |
+ PromiseHandle(value, tasks[i], deferreds[i]); |
+ } |
+ } else { |
+ PromiseHandle(value, tasks, deferreds); |
} |
if (instrumenting) { |
%DebugAsyncTaskEvent({ type: "didHandle", id: id, name: name }); |
@@ -159,6 +180,36 @@ function PromiseEnqueue(value, tasks, status) { |
} |
} |
+function PromiseAttachCallbacks(promise, deferred, onResolve, onReject) { |
+ var maybeResolveCallbacks = |
+ GET_PRIVATE(promise, promiseFulfillReactionsSymbol); |
+ if (IS_UNDEFINED(maybeResolveCallbacks)) { |
+ SET_PRIVATE(promise, promiseFulfillReactionsSymbol, onResolve); |
+ SET_PRIVATE(promise, promiseRejectReactionsSymbol, onReject); |
+ SET_PRIVATE(promise, promiseDeferredReactionsSymbol, deferred); |
+ } else if (!IS_ARRAY(maybeResolveCallbacks)) { |
+ var resolveCallbacks = new InternalArray(); |
+ var rejectCallbacks = new InternalArray(); |
+ var deferreds = new InternalArray(); |
+ |
+ resolveCallbacks.push(maybeResolveCallbacks); |
+ rejectCallbacks.push(GET_PRIVATE(promise, promiseRejectReactionsSymbol)); |
+ deferreds.push(GET_PRIVATE(promise, promiseDeferredReactionsSymbol)); |
+ |
+ resolveCallbacks.push(onResolve); |
+ rejectCallbacks.push(onReject); |
+ deferreds.push(deferred); |
+ |
+ SET_PRIVATE(promise, promiseFulfillReactionsSymbol, resolveCallbacks); |
+ SET_PRIVATE(promise, promiseRejectReactionsSymbol, rejectCallbacks); |
+ SET_PRIVATE(promise, promiseDeferredReactionsSymbol, deferreds); |
+ } else { |
+ maybeResolveCallbacks.push(onResolve); |
+ GET_PRIVATE(promise, promiseRejectReactionsSymbol).push(onReject); |
+ GET_PRIVATE(promise, promiseDeferredReactionsSymbol).push(deferred); |
+ } |
+} |
+ |
function PromiseIdResolveHandler(x) { return x } |
function PromiseIdRejectHandler(r) { throw r } |
@@ -319,14 +370,11 @@ function PromiseThen(onResolve, onReject) { |
var deferred = NewPromiseCapability(constructor); |
switch (status) { |
case kPending: |
- GET_PRIVATE(this, promiseFulfillReactionsSymbol).push(onResolve, |
- deferred); |
- GET_PRIVATE(this, promiseRejectReactionsSymbol).push(onReject, deferred); |
+ PromiseAttachCallbacks(this, deferred, onResolve, onReject); |
break; |
case kFulfilled: |
PromiseEnqueue(GET_PRIVATE(this, promiseResultSymbol), |
- [onResolve, deferred], |
- kFulfilled); |
+ onResolve, deferred, kFulfilled); |
break; |
case kRejected: |
if (!HAS_DEFINED_PRIVATE(this, promiseHasHandlerSymbol)) { |
@@ -335,8 +383,7 @@ function PromiseThen(onResolve, onReject) { |
%PromiseRevokeReject(this); |
} |
PromiseEnqueue(GET_PRIVATE(this, promiseResultSymbol), |
- [onReject, deferred], |
- kRejected); |
+ onReject, deferred, kRejected); |
break; |
} |
// Mark this promise as having handler. |
@@ -445,20 +492,30 @@ function PromiseRace(iterable) { |
// Utility for debugger |
+function PromiseHasUserDefinedRejectHandlerCheck(handler, deferred) { |
+ if (handler !== PromiseIdRejectHandler) { |
+ var combinedDeferred = GET_PRIVATE(handler, promiseCombinedDeferredSymbol); |
+ if (IS_UNDEFINED(combinedDeferred)) return true; |
+ if (PromiseHasUserDefinedRejectHandlerRecursive(combinedDeferred.promise)) { |
+ return true; |
+ } |
+ } else if (PromiseHasUserDefinedRejectHandlerRecursive(deferred.promise)) { |
+ return true; |
+ } |
+ return false; |
+} |
+ |
function PromiseHasUserDefinedRejectHandlerRecursive(promise) { |
var queue = GET_PRIVATE(promise, promiseRejectReactionsSymbol); |
+ var deferreds = GET_PRIVATE(promise, promiseDeferredReactionsSymbol); |
if (IS_UNDEFINED(queue)) return false; |
- for (var i = 0; i < queue.length; i += 2) { |
- var handler = queue[i]; |
- if (handler !== PromiseIdRejectHandler) { |
- var deferred = GET_PRIVATE(handler, promiseCombinedDeferredSymbol); |
- if (IS_UNDEFINED(deferred)) return true; |
- if (PromiseHasUserDefinedRejectHandlerRecursive(deferred.promise)) { |
+ if (!IS_ARRAY(queue)) { |
+ return PromiseHasUserDefinedRejectHandlerCheck(queue, deferreds); |
+ } else { |
+ for (var i = 0; i < queue.length; i += 1) { |
+ if (PromiseHasUserDefinedRejectHandlerCheck(queue[i], deferreds[i])) { |
return true; |
} |
- } else if (PromiseHasUserDefinedRejectHandlerRecursive( |
- queue[i + 1].promise)) { |
- return true; |
} |
} |
return false; |