| Index: src/promise.js
|
| diff --git a/src/promise.js b/src/promise.js
|
| index d202f2886166af38985140786888831c0ba25be5..aa9da2e59495fd83610dac9fcb371f81ee7d6269 100644
|
| --- a/src/promise.js
|
| +++ b/src/promise.js
|
| @@ -9,28 +9,18 @@
|
| // var $Object = global.Object
|
| // var $WeakMap = global.WeakMap
|
|
|
| +// For bootstrapper.
|
|
|
| -var $Promise = function Promise(resolver) {
|
| - if (resolver === promiseRaw) return;
|
| - if (!%_IsConstructCall()) throw MakeTypeError('not_a_promise', [this]);
|
| - if (!IS_SPEC_FUNCTION(resolver))
|
| - throw MakeTypeError('resolver_not_a_function', [resolver]);
|
| - var promise = PromiseInit(this);
|
| - try {
|
| - %DebugPromiseHandlePrologue(function() { return promise });
|
| - resolver(function(x) { PromiseResolve(promise, x) },
|
| - function(r) { PromiseReject(promise, r) });
|
| - } catch (e) {
|
| - PromiseReject(promise, e);
|
| - } finally {
|
| - %DebugPromiseHandleEpilogue();
|
| - }
|
| -}
|
| -
|
| -
|
| -//-------------------------------------------------------------------
|
| +var IsPromise;
|
| +var PromiseCreate;
|
| +var PromiseResolve;
|
| +var PromiseReject;
|
| +var PromiseChain;
|
| +var PromiseCatch;
|
|
|
| -// Core functionality.
|
| +// mirror-debugger.js currently uses builtins.promiseStatus. It would be nice
|
| +// if we could move these property names into the closure below.
|
| +// TODO(jkummerow/rossberg/yangguo): Find a better solution.
|
|
|
| // Status values: 0 = pending, +1 = resolved, -1 = rejected
|
| var promiseStatus = GLOBAL_PRIVATE("Promise#status");
|
| @@ -39,250 +29,274 @@ var promiseOnResolve = GLOBAL_PRIVATE("Promise#onResolve");
|
| var promiseOnReject = GLOBAL_PRIVATE("Promise#onReject");
|
| var promiseRaw = GLOBAL_PRIVATE("Promise#raw");
|
|
|
| -function IsPromise(x) {
|
| - return IS_SPEC_OBJECT(x) && %HasLocalProperty(x, promiseStatus);
|
| -}
|
| -
|
| -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) {
|
| - PromiseEnqueue(value, GET_PRIVATE(promise, promiseQueue));
|
| - PromiseSet(promise, status, value);
|
| +(function() {
|
| +
|
| + var $Promise = function Promise(resolver) {
|
| + if (resolver === promiseRaw) return;
|
| + if (!%_IsConstructCall()) throw MakeTypeError('not_a_promise', [this]);
|
| + if (!IS_SPEC_FUNCTION(resolver))
|
| + throw MakeTypeError('resolver_not_a_function', [resolver]);
|
| + var promise = PromiseInit(this);
|
| + try {
|
| + %DebugPromiseHandlePrologue(function() { return promise });
|
| + resolver(function(x) { PromiseResolve(promise, x) },
|
| + function(r) { PromiseReject(promise, r) });
|
| + } catch (e) {
|
| + PromiseReject(promise, e);
|
| + } finally {
|
| + %DebugPromiseHandleEpilogue();
|
| + }
|
| }
|
| -}
|
|
|
| -function PromiseResolve(promise, x) {
|
| - PromiseDone(promise, +1, x, promiseOnResolve)
|
| -}
|
| + // Core functionality.
|
|
|
| -function PromiseReject(promise, r) {
|
| - PromiseDone(promise, -1, r, promiseOnReject)
|
| -}
|
| + 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)
|
| + }
|
|
|
| -// For API.
|
| + function PromiseDone(promise, status, value, promiseQueue) {
|
| + if (GET_PRIVATE(promise, promiseStatus) === 0) {
|
| + PromiseEnqueue(value, GET_PRIVATE(promise, promiseQueue));
|
| + PromiseSet(promise, status, value);
|
| + }
|
| + }
|
|
|
| -function PromiseNopResolver() {}
|
| + function PromiseCoerce(constructor, x) {
|
| + if (!IsPromise(x) && IS_SPEC_OBJECT(x)) {
|
| + var then;
|
| + try {
|
| + then = x.then;
|
| + } catch(r) {
|
| + return %_CallFunction(constructor, r, PromiseRejected);
|
| + }
|
| + if (IS_SPEC_FUNCTION(then)) {
|
| + var deferred = %_CallFunction(constructor, PromiseDeferred);
|
| + try {
|
| + %_CallFunction(x, deferred.resolve, deferred.reject, then);
|
| + } catch(r) {
|
| + deferred.reject(r);
|
| + }
|
| + return deferred.promise;
|
| + }
|
| + }
|
| + return x;
|
| + }
|
|
|
| -function PromiseCreate() {
|
| - return new $Promise(PromiseNopResolver)
|
| -}
|
| + function PromiseHandle(value, handler, deferred) {
|
| + try {
|
| + %DebugPromiseHandlePrologue(
|
| + function() {
|
| + var queue = GET_PRIVATE(deferred.promise, promiseOnReject);
|
| + return (queue && queue.length == 0) ? deferred.promise : UNDEFINED;
|
| + });
|
| + var result = handler(value);
|
| + if (result === deferred.promise)
|
| + throw MakeTypeError('promise_cyclic', [result]);
|
| + else if (IsPromise(result))
|
| + %_CallFunction(result, deferred.resolve, deferred.reject, PromiseChain);
|
| + else
|
| + deferred.resolve(result);
|
| + } catch (exception) {
|
| + try {
|
| + %DebugPromiseHandlePrologue(function() { return deferred.promise });
|
| + deferred.reject(exception);
|
| + } catch (e) { } finally {
|
| + %DebugPromiseHandleEpilogue();
|
| + }
|
| + } finally {
|
| + %DebugPromiseHandleEpilogue();
|
| + }
|
| + }
|
| +
|
| + function PromiseEnqueue(value, tasks) {
|
| + %EnqueueMicrotask(function() {
|
| + for (var i = 0; i < tasks.length; i += 2) {
|
| + PromiseHandle(value, tasks[i], tasks[i + 1])
|
| + }
|
| + });
|
| + }
|
|
|
| + function PromiseIdResolveHandler(x) { return x }
|
| + function PromiseIdRejectHandler(r) { throw r }
|
|
|
| -// Convenience.
|
| + function PromiseNopResolver() {}
|
|
|
| -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) });
|
| + // -------------------------------------------------------------------
|
| + // Define exported functions.
|
| +
|
| + // For bootstrapper.
|
| +
|
| + IsPromise = function IsPromise(x) {
|
| + return IS_SPEC_OBJECT(x) && %HasLocalProperty(x, promiseStatus);
|
| }
|
| -}
|
| -
|
| -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) });
|
| +
|
| + PromiseCreate = function PromiseCreate() {
|
| + return new $Promise(PromiseNopResolver)
|
| }
|
| -}
|
| -
|
| -
|
| -// 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;
|
| +
|
| + PromiseResolve = function PromiseResolve(promise, x) {
|
| + PromiseDone(promise, +1, x, promiseOnResolve)
|
| }
|
| - return deferred.promise;
|
| -}
|
|
|
| -function PromiseCatch(onReject) {
|
| - return this.then(UNDEFINED, onReject);
|
| -}
|
| + PromiseReject = function PromiseReject(promise, r) {
|
| + PromiseDone(promise, -1, r, promiseOnReject)
|
| + }
|
|
|
| -function PromiseEnqueue(value, tasks) {
|
| - %EnqueueMicrotask(function() {
|
| - for (var i = 0; i < tasks.length; i += 2) {
|
| - PromiseHandle(value, tasks[i], tasks[i + 1])
|
| + // 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 PromiseHandle(value, handler, deferred) {
|
| - try {
|
| - %DebugPromiseHandlePrologue(
|
| - function() {
|
| - var queue = GET_PRIVATE(deferred.promise, promiseOnReject);
|
| - return (queue && queue.length == 0) ? deferred.promise : UNDEFINED;
|
| - });
|
| - var result = handler(value);
|
| - if (result === deferred.promise)
|
| - throw MakeTypeError('promise_cyclic', [result]);
|
| - else if (IsPromise(result))
|
| - %_CallFunction(result, deferred.resolve, deferred.reject, PromiseChain);
|
| - else
|
| - deferred.resolve(result);
|
| - } catch (exception) {
|
| - try {
|
| - %DebugPromiseHandlePrologue(function() { return deferred.promise });
|
| - deferred.reject(exception);
|
| - } catch (e) { } finally {
|
| - %DebugPromiseHandleEpilogue();
|
| + }
|
| +
|
| + 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) });
|
| }
|
| - } finally {
|
| - %DebugPromiseHandleEpilogue();
|
| }
|
| -}
|
| -
|
| -
|
| -// Multi-unwrapped chaining with thenable coercion.
|
| -
|
| -function PromiseThen(onResolve, onReject) {
|
| - onResolve = IS_SPEC_FUNCTION(onResolve) ? onResolve : PromiseIdResolveHandler;
|
| - onReject = IS_SPEC_FUNCTION(onReject) ? onReject : PromiseIdRejectHandler;
|
| - var that = this;
|
| - var constructor = this.constructor;
|
| - return %_CallFunction(
|
| - this,
|
| - function(x) {
|
| - x = PromiseCoerce(constructor, x);
|
| - return x === that ? onReject(MakeTypeError('promise_cyclic', [x])) :
|
| - IsPromise(x) ? x.then(onResolve, onReject) : onResolve(x);
|
| - },
|
| - onReject,
|
| - PromiseChain
|
| - );
|
| -}
|
| -
|
| -function PromiseCoerce(constructor, x) {
|
| - if (!IsPromise(x) && IS_SPEC_OBJECT(x)) {
|
| - var then;
|
| - try {
|
| - then = x.then;
|
| - } catch(r) {
|
| - return %_CallFunction(constructor, r, PromiseRejected);
|
| +
|
| + 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) });
|
| }
|
| - if (IS_SPEC_FUNCTION(then)) {
|
| - var deferred = %_CallFunction(constructor, PromiseDeferred);
|
| - try {
|
| - %_CallFunction(x, deferred.resolve, deferred.reject, then);
|
| - } catch(r) {
|
| - deferred.reject(r);
|
| - }
|
| - return deferred.promise;
|
| + }
|
| +
|
| + // Simple chaining.
|
| +
|
| + PromiseChain = 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;
|
| }
|
| - return x;
|
| -}
|
|
|
| + PromiseCatch = function PromiseCatch(onReject) {
|
| + return this.then(UNDEFINED, onReject);
|
| + }
|
|
|
| -// Combinators.
|
| + // Multi-unwrapped chaining with thenable coercion.
|
| +
|
| + function PromiseThen(onResolve, onReject) {
|
| + onResolve = IS_SPEC_FUNCTION(onResolve) ? onResolve
|
| + : PromiseIdResolveHandler;
|
| + onReject = IS_SPEC_FUNCTION(onReject) ? onReject
|
| + : PromiseIdRejectHandler;
|
| + var that = this;
|
| + var constructor = this.constructor;
|
| + return %_CallFunction(
|
| + this,
|
| + function(x) {
|
| + x = PromiseCoerce(constructor, x);
|
| + return x === that ? onReject(MakeTypeError('promise_cyclic', [x])) :
|
| + IsPromise(x) ? x.then(onResolve, onReject) : onResolve(x);
|
| + },
|
| + onReject,
|
| + PromiseChain
|
| + );
|
| + }
|
|
|
| -function PromiseCast(x) {
|
| - // TODO(rossberg): cannot do better until we support @@create.
|
| - return IsPromise(x) ? x : new this(function(resolve) { resolve(x) });
|
| -}
|
| + // Combinators.
|
|
|
| -function PromiseAll(values) {
|
| - var deferred = %_CallFunction(this, PromiseDeferred);
|
| - var resolutions = [];
|
| - if (!%_IsArray(values)) {
|
| - deferred.reject(MakeTypeError('invalid_argument'));
|
| + function PromiseCast(x) {
|
| + // TODO(rossberg): cannot do better until we support @@create.
|
| + return IsPromise(x) ? x : new this(function(resolve) { resolve(x) });
|
| + }
|
| +
|
| + function PromiseAll(values) {
|
| + var deferred = %_CallFunction(this, PromiseDeferred);
|
| + var resolutions = [];
|
| + if (!%_IsArray(values)) {
|
| + deferred.reject(MakeTypeError('invalid_argument'));
|
| + return deferred.promise;
|
| + }
|
| + try {
|
| + var count = values.length;
|
| + if (count === 0) {
|
| + deferred.resolve(resolutions);
|
| + } else {
|
| + for (var i = 0; i < values.length; ++i) {
|
| + this.resolve(values[i]).then(
|
| + function(i, x) {
|
| + resolutions[i] = x;
|
| + if (--count === 0) deferred.resolve(resolutions);
|
| + }.bind(UNDEFINED, i), // TODO(rossberg): use let loop once
|
| + // available
|
| + function(r) { deferred.reject(r) }
|
| + );
|
| + }
|
| + }
|
| + } catch (e) {
|
| + deferred.reject(e)
|
| + }
|
| return deferred.promise;
|
| }
|
| - try {
|
| - var count = values.length;
|
| - if (count === 0) {
|
| - deferred.resolve(resolutions);
|
| - } else {
|
| +
|
| + function PromiseOne(values) {
|
| + var deferred = %_CallFunction(this, PromiseDeferred);
|
| + if (!%_IsArray(values)) {
|
| + deferred.reject(MakeTypeError('invalid_argument'));
|
| + return deferred.promise;
|
| + }
|
| + try {
|
| for (var i = 0; i < values.length; ++i) {
|
| this.resolve(values[i]).then(
|
| - function(i, x) {
|
| - resolutions[i] = x;
|
| - if (--count === 0) deferred.resolve(resolutions);
|
| - }.bind(UNDEFINED, i), // TODO(rossberg): use let loop once available
|
| + function(x) { deferred.resolve(x) },
|
| function(r) { deferred.reject(r) }
|
| );
|
| }
|
| + } catch (e) {
|
| + deferred.reject(e)
|
| }
|
| - } catch (e) {
|
| - deferred.reject(e)
|
| - }
|
| - return deferred.promise;
|
| -}
|
| -
|
| -function PromiseOne(values) {
|
| - var deferred = %_CallFunction(this, PromiseDeferred);
|
| - if (!%_IsArray(values)) {
|
| - deferred.reject(MakeTypeError('invalid_argument'));
|
| return deferred.promise;
|
| }
|
| - try {
|
| - for (var i = 0; i < values.length; ++i) {
|
| - this.resolve(values[i]).then(
|
| - function(x) { deferred.resolve(x) },
|
| - function(r) { deferred.reject(r) }
|
| - );
|
| - }
|
| - } catch (e) {
|
| - deferred.reject(e)
|
| - }
|
| - return deferred.promise;
|
| -}
|
|
|
| -//-------------------------------------------------------------------
|
| + // -------------------------------------------------------------------
|
| + // Install exported functions.
|
|
|
| -function SetUpPromise() {
|
| %CheckIsBootstrapping();
|
| %SetProperty(global, 'Promise', $Promise, DONT_ENUM);
|
| InstallFunctions($Promise, DONT_ENUM, [
|
| @@ -298,6 +312,5 @@ function SetUpPromise() {
|
| "then", PromiseThen,
|
| "catch", PromiseCatch
|
| ]);
|
| -}
|
|
|
| -SetUpPromise();
|
| +})();
|
|
|