Chromium Code Reviews| Index: src/js/promise.js |
| diff --git a/src/js/promise.js b/src/js/promise.js |
| index de9597942c010f52d6dd996fc5faac2000b628ec..a165817b7160ec3798f34e01917f5f4a8467a27b 100644 |
| --- a/src/js/promise.js |
| +++ b/src/js/promise.js |
| @@ -40,9 +40,6 @@ function CreateResolvingFunctions(promise) { |
| var resolve = function(value) { |
| if (alreadyResolved === true) return; |
| alreadyResolved = true; |
| - if (value === promise) { |
| - return PromiseReject(promise, MakeTypeError(kPromiseCyclic, value)); |
| - } |
| PromiseResolve(promise, value); |
| }; |
| @@ -116,37 +113,11 @@ function PromiseDone(promise, status, value, promiseQueue) { |
| } |
| } |
| -function PromiseCoerce(constructor, x) { |
| - if (!IsPromise(x) && IS_RECEIVER(x)) { |
| - var then; |
| - try { |
| - then = x.then; |
| - } catch(r) { |
| - return %_Call(PromiseRejected, constructor, r); |
| - } |
| - if (IS_CALLABLE(then)) { |
| - var deferred = NewPromiseCapability(constructor); |
| - try { |
| - %_Call(then, x, deferred.resolve, deferred.reject); |
| - } catch(r) { |
| - deferred.reject(r); |
| - } |
| - return deferred.promise; |
| - } |
| - } |
| - return x; |
| -} |
| - |
| function PromiseHandle(value, handler, deferred) { |
| try { |
| %DebugPushPromise(deferred.promise, PromiseHandle); |
| var result = handler(value); |
| - if (result === deferred.promise) |
| - throw MakeTypeError(kPromiseCyclic, result); |
| - else if (IsPromise(result)) |
| - %_Call(PromiseChain, result, deferred.resolve, deferred.reject); |
| - else |
| - deferred.resolve(result); |
| + deferred.resolve(result); |
| } catch (exception) { |
| try { deferred.reject(exception); } catch (e) { } |
| } finally { |
| @@ -193,28 +164,42 @@ function PromiseCreate() { |
| } |
| function PromiseResolve(promise, x) { |
| - if (GET_PRIVATE(promise, promiseStatusSymbol) === 0) { |
| - if (IS_RECEIVER(x)) { |
| - // 25.4.1.3.2 steps 8-12 |
| - try { |
| - var then = x.then; |
| - } catch (e) { |
| - return PromiseReject(promise, e); |
| - } |
| - if (IS_CALLABLE(then)) { |
| - // PromiseResolveThenableJob |
| - return %EnqueueMicrotask(function() { |
| - try { |
| - var callbacks = CreateResolvingFunctions(promise); |
| - %_Call(then, x, callbacks.resolve, callbacks.reject); |
| - } catch (e) { |
| - PromiseReject(promise, e); |
| - } |
| - }); |
| + if (x === promise) { |
| + return PromiseReject(promise, MakeTypeError(kPromiseCyclic, x)); |
| + } |
| + if (IS_RECEIVER(x)) { |
|
Camillo Bruni
2015/12/28 12:22:35
Early return would make this more readable, and ea
|
| + // 25.4.1.3.2 steps 8-12 |
| + try { |
| + var then = x.then; |
| + } catch (e) { |
| + return PromiseReject(promise, e); |
| + } |
| + if (IS_CALLABLE(then)) { |
|
Camillo Bruni
2015/12/28 12:22:35
I'd say the same here ;) (yes, yes nit-picking :P)
|
| + // PromiseResolveThenableJob |
| + var id, name, instrumenting = DEBUG_IS_ACTIVE; |
| + %EnqueueMicrotask(function() { |
| + if (instrumenting) { |
| + %DebugAsyncTaskEvent({ type: "willHandle", id: id, name: name }); |
| + } |
| + try { |
| + var callbacks = CreateResolvingFunctions(promise); |
| + %_Call(then, x, callbacks.resolve, callbacks.reject); |
| + } catch (e) { |
| + PromiseReject(promise, e); |
| + } |
| + if (instrumenting) { |
| + %DebugAsyncTaskEvent({ type: "didHandle", id: id, name: name }); |
| + } |
| + }); |
| + if (instrumenting) { |
| + id = ++lastMicrotaskId; |
| + name = "PromseResolveThenableJob"; |
| + %DebugAsyncTaskEvent({ type: "enqueue", id: id, name: name }); |
| } |
| + return; |
| } |
| - PromiseDone(promise, +1, x, promiseOnResolveSymbol); |
| } |
| + PromiseDone(promise, +1, x, promiseOnResolveSymbol); |
| } |
| function PromiseReject(promise, r) { |
| @@ -245,6 +230,7 @@ function NewPromiseCapability(C) { |
| } else { |
| var result = {promise: UNDEFINED, resolve: UNDEFINED, reject: UNDEFINED }; |
| result.promise = new C(function(resolve, reject) { |
| + // TODO(littledan): Check for resolve and reject being not undefined |
| result.resolve = resolve; |
| result.reject = reject; |
| }); |
| @@ -257,15 +243,13 @@ function PromiseDeferred() { |
| } |
| function PromiseResolved(x) { |
| - if (this === GlobalPromise) { |
| - // Optimized case, avoid extra closure. |
| - return PromiseCreateAndSet(+1, x); |
| - } else { |
| - return new this(function(resolve, reject) { resolve(x) }); |
| - } |
| + return %_Call(PromiseCast, this, x); |
| } |
| function PromiseRejected(r) { |
|
Camillo Bruni
2015/12/28 12:22:35
Dangerous name if you ask me :) PromiseReject vs.
|
| + if (!IS_RECEIVER(this)) { |
| + throw MakeTypeError(kCalledOnNonObject, PromiseRejected); |
| + } |
| var promise; |
| if (this === GlobalPromise) { |
| // Optimized case, avoid extra closure. |
| @@ -279,15 +263,18 @@ function PromiseRejected(r) { |
| return promise; |
| } |
| -// Simple chaining. |
| +// Multi-unwrapped chaining with thenable coercion. |
| -// PromiseChain a.k.a. flatMap |
| -function PromiseChainInternal(constructor, onResolve, onReject) { |
| - onResolve = IS_UNDEFINED(onResolve) ? PromiseIdResolveHandler : onResolve; |
| - onReject = IS_UNDEFINED(onReject) ? PromiseIdRejectHandler : onReject; |
| +function PromiseThen(onResolve, onReject) { |
| + var constructor = this.constructor; |
| + onResolve = IS_CALLABLE(onResolve) ? onResolve : PromiseIdResolveHandler; |
| + onReject = IS_CALLABLE(onReject) ? onReject : PromiseIdRejectHandler; |
| var deferred = NewPromiseCapability(constructor); |
| switch (GET_PRIVATE(this, promiseStatusSymbol)) { |
| case UNDEFINED: |
| + // TODO(littledan): The type check should be called before |
| + // constructing NewPromiseCapability; this is observable when |
| + // erroneously copying this method to other classes. |
| throw MakeTypeError(kNotAPromise, this); |
| case 0: // Pending |
| GET_PRIVATE(this, promiseOnResolveSymbol).push(onResolve, deferred); |
| @@ -317,47 +304,25 @@ function PromiseChainInternal(constructor, onResolve, onReject) { |
| return deferred.promise; |
| } |
| +// Chain is left around for now as an alias for then |
| function PromiseChain(onResolve, onReject) { |
| - return %_Call(PromiseChainInternal, this, this.constructor, |
| - onResolve, onReject); |
| + return %_Call(PromiseThen, this, onResolve, onReject); |
| } |
| function PromiseCatch(onReject) { |
| return this.then(UNDEFINED, onReject); |
| } |
| -// Multi-unwrapped chaining with thenable coercion. |
| - |
| -function PromiseThen(onResolve, onReject) { |
| - onResolve = IS_CALLABLE(onResolve) ? onResolve : PromiseIdResolveHandler; |
| - onReject = IS_CALLABLE(onReject) ? onReject : PromiseIdRejectHandler; |
| - var that = this; |
| - var constructor = this.constructor; |
| - return %_Call( |
| - PromiseChainInternal, |
| - this, |
| - constructor, |
| - function(x) { |
| - x = PromiseCoerce(constructor, x); |
| - if (x === that) { |
| - return onReject(MakeTypeError(kPromiseCyclic, x)); |
| - } else if (IsPromise(x)) { |
| - return x.then(onResolve, onReject); |
| - } else { |
| - return onResolve(x); |
| - } |
| - }, |
| - onReject |
| - ); |
| -} |
| - |
| // Combinators. |
| function PromiseCast(x) { |
|
Camillo Bruni
2015/12/28 12:22:35
I know this is probably not very important, but as
|
| + if (!IS_RECEIVER(this)) { |
| + throw MakeTypeError(kCalledOnNonObject, PromiseCast); |
| + } |
| if (IsPromise(x) && x.constructor === this) { |
| return x; |
| } else { |
| - return new this(function(resolve) { resolve(x) }); |
| + return new this(function(resolve, reject) { resolve(x) }); |
| } |
| } |