| Index: src/js/promise.js
|
| diff --git a/src/js/promise.js b/src/js/promise.js
|
| index f59809f0bd6034cd7b6e6f3beb977e89fd0c4995..1016cbba29be3fbf6afa3cdd8e159a5517a7d96f 100644
|
| --- a/src/js/promise.js
|
| +++ b/src/js/promise.js
|
| @@ -40,6 +40,9 @@
|
| var resolve = function(value) {
|
| if (alreadyResolved === true) return;
|
| alreadyResolved = true;
|
| + if (value === promise) {
|
| + return PromiseReject(promise, MakeTypeError(kPromiseCyclic, value));
|
| + }
|
| PromiseResolve(promise, value);
|
| };
|
|
|
| @@ -113,11 +116,37 @@
|
| }
|
| }
|
|
|
| +function PromiseCoerce(constructor, x) {
|
| + if (!IsPromise(x) && IS_SPEC_OBJECT(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);
|
| - deferred.resolve(result);
|
| + if (result === deferred.promise)
|
| + throw MakeTypeError(kPromiseCyclic, result);
|
| + else if (IsPromise(result))
|
| + %_Call(PromiseChain, result, deferred.resolve, deferred.reject);
|
| + else
|
| + deferred.resolve(result);
|
| } catch (exception) {
|
| try { deferred.reject(exception); } catch (e) { }
|
| } finally {
|
| @@ -164,29 +193,28 @@
|
| }
|
|
|
| function PromiseResolve(promise, x) {
|
| - if (x === promise) {
|
| - return PromiseReject(promise, MakeTypeError(kPromiseCyclic, x));
|
| - }
|
| - if (IS_SPEC_OBJECT(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);
|
| - }
|
| - });
|
| - }
|
| - }
|
| - PromiseDone(promise, +1, x, promiseOnResolveSymbol);
|
| + if (GET_PRIVATE(promise, promiseStatusSymbol) === 0) {
|
| + if (IS_SPEC_OBJECT(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);
|
| + }
|
| + });
|
| + }
|
| + }
|
| + PromiseDone(promise, +1, x, promiseOnResolveSymbol);
|
| + }
|
| }
|
|
|
| function PromiseReject(promise, r) {
|
| @@ -217,7 +245,6 @@
|
| } 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;
|
| });
|
| @@ -230,13 +257,15 @@
|
| }
|
|
|
| function PromiseResolved(x) {
|
| - return %_Call(PromiseCast, this, x);
|
| + if (this === GlobalPromise) {
|
| + // Optimized case, avoid extra closure.
|
| + return PromiseCreateAndSet(+1, x);
|
| + } else {
|
| + return new this(function(resolve, reject) { resolve(x) });
|
| + }
|
| }
|
|
|
| function PromiseRejected(r) {
|
| - if (!IS_SPEC_OBJECT(this)) {
|
| - throw MakeTypeError(kCalledOnNonObject, PromiseRejected);
|
| - }
|
| var promise;
|
| if (this === GlobalPromise) {
|
| // Optimized case, avoid extra closure.
|
| @@ -250,18 +279,15 @@
|
| return promise;
|
| }
|
|
|
| -// Multi-unwrapped chaining with thenable coercion.
|
| -
|
| -function PromiseThen(onResolve, onReject) {
|
| - var constructor = this.constructor;
|
| - onResolve = IS_CALLABLE(onResolve) ? onResolve : PromiseIdResolveHandler;
|
| - onReject = IS_CALLABLE(onReject) ? onReject : PromiseIdRejectHandler;
|
| +// Simple chaining.
|
| +
|
| +// PromiseChain a.k.a. flatMap
|
| +function PromiseChainInternal(constructor, onResolve, onReject) {
|
| + onResolve = IS_UNDEFINED(onResolve) ? PromiseIdResolveHandler : onResolve;
|
| + onReject = IS_UNDEFINED(onReject) ? PromiseIdRejectHandler : onReject;
|
| 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);
|
| @@ -291,25 +317,47 @@
|
| return deferred.promise;
|
| }
|
|
|
| -// Chain is left around for now as an alias for then
|
| function PromiseChain(onResolve, onReject) {
|
| - return %_Call(PromiseThen, this, onResolve, onReject);
|
| + return %_Call(PromiseChainInternal, this, this.constructor,
|
| + 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) {
|
| - if (!IS_SPEC_OBJECT(this)) {
|
| - throw MakeTypeError(kCalledOnNonObject, PromiseCast);
|
| - }
|
| if (IsPromise(x) && x.constructor === this) {
|
| return x;
|
| } else {
|
| - return new this(function(resolve, reject) { resolve(x) });
|
| + return new this(function(resolve) { resolve(x) });
|
| }
|
| }
|
|
|
|
|