Index: src/js/promise.js |
diff --git a/src/js/promise.js b/src/js/promise.js |
index c057238af696d78416a6198b3c0f68dabb8c31c1..23086b5054c7a7f28bf7aa62d9baaaf07f69fbcd 100644 |
--- a/src/js/promise.js |
+++ b/src/js/promise.js |
@@ -14,6 +14,8 @@ |
var InternalArray = utils.InternalArray; |
var promiseCombinedDeferredSymbol = |
utils.ImportNow("promise_combined_deferred_symbol"); |
+var promiseResolvingFunctionCalledSymbol = |
+ utils.ImportNow("promise_resolving_function_called_symbol"); |
var promiseHasHandlerSymbol = |
utils.ImportNow("promise_has_handler_symbol"); |
var promiseRejectReactionsSymbol = |
@@ -42,34 +44,6 @@ const kRejected = -1; |
var lastMicrotaskId = 0; |
-// ES#sec-createresolvingfunctions |
-// CreateResolvingFunctions ( promise ) |
-function CreateResolvingFunctions(promise, debugEvent) { |
Dan Ehrenberg
2016/08/30 22:29:28
The spec calls this in two places, in the promise
|
- var alreadyResolved = false; |
- |
- // ES#sec-promise-resolve-functions |
- // Promise Resolve Functions |
- var resolve = value => { |
- if (alreadyResolved === true) return; |
- alreadyResolved = true; |
- ResolvePromise(promise, value); |
- }; |
- |
- // ES#sec-promise-reject-functions |
- // Promise Reject Functions |
- var reject = reason => { |
- if (alreadyResolved === true) return; |
- alreadyResolved = true; |
- RejectPromise(promise, reason, debugEvent); |
- }; |
- |
- return { |
- __proto__: null, |
- resolve: resolve, |
- reject: reject |
- }; |
-} |
- |
// ES#sec-promise-executor |
// Promise ( executor ) |
@@ -84,13 +58,14 @@ var GlobalPromise = function Promise(executor) { |
var promise = PromiseInit(%_NewObject(GlobalPromise, new.target)); |
// Calling the reject function would be a new exception, so debugEvent = true |
- var callbacks = CreateResolvingFunctions(promise, true); |
var debug_is_active = DEBUG_IS_ACTIVE; |
+ var reject = reason => RejectPromise(promise, reason, true, true); |
Dan Ehrenberg
2016/08/30 22:29:28
I wonder, how is performance if you call Function.
|
try { |
if (debug_is_active) %DebugPushPromise(promise); |
- executor(callbacks.resolve, callbacks.reject); |
+ executor(value => ResolvePromise(promise, value, true), |
+ reject); |
} %catch (e) { // Natives syntax to mark this catch block. |
- %_Call(callbacks.reject, UNDEFINED, e); |
+ %_Call(reject, UNDEFINED, e); |
} finally { |
if (debug_is_active) %DebugPopPromise(); |
} |
@@ -126,6 +101,7 @@ function PromiseSet(promise, status, value) { |
// the deferred symbol's state is stale, and the deferreds should be |
// read from the reject, resolve callbacks. |
SET_PRIVATE(promise, promiseDeferredReactionsSymbol, UNDEFINED); |
+ SET_PRIVATE(promise, promiseResolvingFunctionCalledSymbol, UNDEFINED); |
return promise; |
} |
@@ -237,12 +213,16 @@ function PromiseCreate() { |
// ES#sec-promise-resolve-functions |
// Promise Resolve Functions, steps 6-13 |
-function ResolvePromise(promise, resolution) { |
+function ResolvePromise(promise, resolution, follow) { |
+ var status = GET_PRIVATE(promise, promiseResolvingFunctionCalledSymbol); |
+ if (!IS_UNDEFINED(status) && follow) return; |
+ |
if (resolution === promise) { |
return RejectPromise(promise, |
%make_type_error(kPromiseCyclic, resolution), |
true); |
} |
+ |
if (IS_RECEIVER(resolution)) { |
// 25.4.1.3.2 steps 8-12 |
try { |
@@ -283,17 +263,21 @@ function ResolvePromise(promise, resolution) { |
var id; |
var name = "PromiseResolveThenableJob"; |
var instrumenting = DEBUG_IS_ACTIVE; |
+ SET_PRIVATE(promise, promiseResolvingFunctionCalledSymbol, true); |
+ |
%EnqueueMicrotask(function() { |
if (instrumenting) { |
%DebugAsyncTaskEvent({ type: "willHandle", id: id, name: name }); |
} |
// These resolving functions simply forward the exception, so |
// don't create a new debugEvent. |
- var callbacks = CreateResolvingFunctions(promise, false); |
+ |
try { |
- %_Call(then, resolution, callbacks.resolve, callbacks.reject); |
+ %_Call(then, resolution, |
+ value => ResolvePromise(promise, value, false), |
+ reason => RejectPromise(promise, reason, false, false)); |
} catch (e) { |
- %_Call(callbacks.reject, UNDEFINED, e); |
+ %_Call(reason => RejectPromise(promise, reason, false, true), UNDEFINED, e); |
} |
if (instrumenting) { |
%DebugAsyncTaskEvent({ type: "didHandle", id: id, name: name }); |
@@ -311,13 +295,18 @@ function ResolvePromise(promise, resolution) { |
// ES#sec-rejectpromise |
// RejectPromise ( promise, reason ) |
-function RejectPromise(promise, reason, debugEvent) { |
+function RejectPromise(promise, reason, debugEvent, follow) { |
Dan Ehrenberg
2016/08/30 22:29:28
I know I made things bad with the debugEvent param
|
+ var status = GET_PRIVATE(promise, promiseResolvingFunctionCalledSymbol); |
+ if (!IS_UNDEFINED(status) && follow) return; |
+ |
+ status = GET_PRIVATE(promise, promiseStateSymbol); |
+ if (status === kFulfilled) return; |
adamk
2016/08/30 23:00:35
This looks like an unrelated bit of inlining, cons
|
// Check promise status to confirm that this reject has an effect. |
// Call runtime for callbacks to the debugger or for unhandled reject. |
// The debugEvent parameter sets whether a debug ExceptionEvent should |
// be triggered. It should be set to false when forwarding a rejection |
// rather than creating a new one. |
- if (GET_PRIVATE(promise, promiseStateSymbol) === kPending) { |
+ if (status === kPending) { |
// This check is redundant with checks in the runtime, but it may help |
// avoid unnecessary runtime calls. |
if ((debugEvent && DEBUG_IS_ACTIVE) || |
@@ -325,6 +314,7 @@ function RejectPromise(promise, reason, debugEvent) { |
%PromiseRejectEvent(promise, reason, debugEvent); |
} |
} |
+ |
FulfillPromise(promise, kRejected, reason, promiseRejectReactionsSymbol) |
} |
@@ -339,11 +329,10 @@ function NewPromiseCapability(C, debugEvent) { |
if (C === GlobalPromise) { |
// Optimized case, avoid extra closure. |
var promise = PromiseInit(new GlobalPromise(promiseRawSymbol)); |
- var callbacks = CreateResolvingFunctions(promise, debugEvent); |
return { |
promise: promise, |
- resolve: callbacks.resolve, |
- reject: callbacks.reject |
+ resolve: value => ResolvePromise(promise, value, true), |
+ reject: reason => RejectPromise(promise, reason, debugEvent, true) |
}; |
} |