var PromiseChain;
var PromiseCatch;
var PromiseThen;
+var PromiseHasRejectHandler;
// mirror-debugger.js currently uses builtins.promiseStatus. It would be nice
// if we could move these property names into the closure below.
var promiseOnResolve = GLOBAL_PRIVATE("Promise#onResolve");
var promiseOnReject = GLOBAL_PRIVATE("Promise#onReject");
var promiseRaw = GLOBAL_PRIVATE("Promise#raw");
+var promiseDebug = GLOBAL_PRIVATE("Promise#debug");
+var lastMicrotaskId = 0;
(function() {
throw MakeTypeError('resolver_not_a_function', [resolver]);
var promise = PromiseInit(this);
try {
- %DebugPromiseHandlePrologue(function() { return promise });
+ %DebugPushPromise(promise);
resolver(function(x) { PromiseResolve(promise, x) },
function(r) { PromiseReject(promise, r) });
} catch (e) {
PromiseReject(promise, e);
} finally {
- %DebugPromiseHandleEpilogue();
+ %DebugPopPromise();
}
}
SET_PRIVATE(promise, promiseValue, value);
SET_PRIVATE(promise, promiseOnResolve, onResolve);
SET_PRIVATE(promise, promiseOnReject, onReject);
+ if (DEBUG_IS_ACTIVE) {
+ %DebugPromiseEvent({ promise: promise, status: status, value: value });
+ }
return promise;
}
function PromiseDone(promise, status, value, promiseQueue) {
if (GET_PRIVATE(promise, promiseStatus) === 0) {
- PromiseEnqueue(value, GET_PRIVATE(promise, promiseQueue));
+ PromiseEnqueue(value, GET_PRIVATE(promise, promiseQueue), status);
PromiseSet(promise, status, value);
}
}
function PromiseHandle(value, handler, deferred) {
try {
- %DebugPromiseHandlePrologue(
- function() {
- var queue = GET_PRIVATE(deferred.promise, promiseOnReject);
- return (queue && queue.length == 0) ? deferred.promise : UNDEFINED;
- });
+ %DebugPushPromise(deferred.promise);
var result = handler(value);
if (result === deferred.promise)
throw MakeTypeError('promise_cyclic', [result]);
else
deferred.resolve(result);
} catch (exception) {
- try {
- %DebugPromiseHandlePrologue(function() { return deferred.promise });
- deferred.reject(exception);
- } catch (e) { } finally {
- %DebugPromiseHandleEpilogue();
- }
+ try { deferred.reject(exception); } catch (e) { }
} finally {
- %DebugPromiseHandleEpilogue();
+ %DebugPopPromise();
}
}
- function PromiseEnqueue(value, tasks) {
+ function PromiseEnqueue(value, tasks, 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 (instrumenting) {
+ %DebugAsyncTaskEvent({ type: "didHandle", id: id, name: name });
+ }
});
+ if (instrumenting) {
+ id = ++lastMicrotaskId;
+ name = status > 0 ? "Promise.resolve" : "Promise.reject";
+ %DebugAsyncTaskEvent({ type: "enqueue", id: id, name: name });
+ }
}
function PromiseIdResolveHandler(x) { return x }
}
PromiseReject = function PromiseReject(promise, r) {
+ // Check promise status to confirm that this reject has an effect.
+ // Check promiseDebug property to avoid duplicate event.
+ if (DEBUG_IS_ACTIVE &&
+ GET_PRIVATE(promise, promiseStatus) == 0 &&
+ !HAS_PRIVATE(promise, promiseDebug)) {
+ %DebugPromiseRejectEvent(promise, r);
+ }
PromiseDone(promise, -1, r, promiseOnReject)
}
// Simple chaining.
PromiseChain = function PromiseChain(onResolve, onReject) { // a.k.a.
- // flatMap
+ // flatMap
onResolve = IS_UNDEFINED(onResolve) ? PromiseIdResolveHandler : onResolve;
onReject = IS_UNDEFINED(onReject) ? PromiseIdRejectHandler : onReject;
var deferred = %_CallFunction(this.constructor, PromiseDeferred);
GET_PRIVATE(this, promiseOnReject).push(onReject, deferred);
break;
case +1: // Resolved
- PromiseEnqueue(GET_PRIVATE(this, promiseValue), [onResolve, deferred]);
+ PromiseEnqueue(GET_PRIVATE(this, promiseValue),
+ [onResolve, deferred],
+ +1);
break;
case -1: // Rejected
- PromiseEnqueue(GET_PRIVATE(this, promiseValue), [onReject, deferred]);
+ PromiseEnqueue(GET_PRIVATE(this, promiseValue),
+ [onReject, deferred],
+ -1);
break;
}
+ if (DEBUG_IS_ACTIVE) {
+ %DebugPromiseEvent({ promise: deferred.promise, parentPromise: this });
+ }
return deferred.promise;
}
} 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() {
+ // Nested scope to get closure over current i (and avoid .bind).
+ // TODO(rossberg): Use for-let instead once available.
+ var i_captured = i;
+ return function(x) {
+ resolutions[i_captured] = x;
+ if (--count === 0) deferred.resolve(resolutions);
+ };
+ })(),
function(r) { deferred.reject(r) }
);
}
return deferred.promise;
}
+
+ // Utility for debugger
+
+ PromiseHasRejectHandler = function PromiseHasRejectHandler() {
+ // Mark promise as already having triggered a reject event.
+ SET_PRIVATE(this, promiseDebug, true);
+ var queue = GET_PRIVATE(this, promiseOnReject);
+ return !IS_UNDEFINED(queue) && queue.length > 0;
+ };
+
// -------------------------------------------------------------------
// Install exported functions.
%CheckIsBootstrapping();
- %SetProperty(global, 'Promise', $Promise, DONT_ENUM);
+ %AddNamedProperty(global, 'Promise', $Promise, DONT_ENUM);
InstallFunctions($Promise, DONT_ENUM, [
"defer", PromiseDeferred,
"accept", PromiseResolved,