Source code
Revision control
Copy as Markdown
Other Tools
// |jit-test| skip-if: getBuildConfiguration("release_or_beta"); --enable-promise-safe-resolve
//
// resolve(thenable, true) adds exactly one extra microtask tick relative to
// resolve(thenable) — the DeferredResolveJob runs first, enqueues the
// normal PromiseResolveThenableJob, which then fulfills the outer promise.
// Baseline: resolve with a callable-then thenable, no safe-resolve.
// Tick 0 (sync): Get("then"), enqueue ThenableJob.
// Tick 1: ThenableJob runs -> thenable.then(rF, rJ) -> rF("v") -> fulfill.
// Tick 2: reaction fires.
{
const thenable = {
then(onFulfilled) { onFulfilled("baseline"); },
};
const {promise, resolve} = Promise.withResolvers();
resolve(thenable); // no safe-resolve
let ticksAtFulfill = -1;
let tick = 0;
promise.then(() => { ticksAtFulfill = tick; });
// Advance a bounded number of microtask turns and see when the reaction
// runs. Build a chain using native Promises (not the synthetic one).
let pending = Promise.resolve();
for (let i = 0; i < 5; i++) {
pending = pending.then(() => { tick++; });
}
drainJobQueue();
assertEq(ticksAtFulfill >= 0, true, "reaction fired at some tick");
// Record baseline tick count.
var baselineTick = ticksAtFulfill;
}
// With safe-resolve: one extra tick.
{
const thenable = {
then(onFulfilled) { onFulfilled("safe"); },
};
const {promise, resolve} = Promise.withResolvers();
resolve(thenable, true);
let ticksAtFulfill = -1;
let tick = 0;
promise.then(() => { ticksAtFulfill = tick; });
let pending = Promise.resolve();
for (let i = 0; i < 5; i++) {
pending = pending.then(() => { tick++; });
}
drainJobQueue();
assertEq(ticksAtFulfill >= 0, true, "reaction fired at some tick");
assertEq(ticksAtFulfill, baselineTick + 1,
"resolve(thenable, true) takes exactly one extra microtask tick");
}
// Real Promise-with-Promise via safe-resolve: same +1 tick invariant.
{
const inner = Promise.resolve("inner");
const {promise: outerBase, resolve: rBase} = Promise.withResolvers();
rBase(inner); // baseline
let baseTick = -1, tick = 0;
outerBase.then(() => { baseTick = tick; });
let chain = Promise.resolve();
for (let i = 0; i < 6; i++) chain = chain.then(() => { tick++; });
drainJobQueue();
const inner2 = Promise.resolve("inner");
const {promise: outerSafe, resolve: rSafe} = Promise.withResolvers();
rSafe(inner2, true);
let safeTick = -1, tick2 = 0;
outerSafe.then(() => { safeTick = tick2; });
let chain2 = Promise.resolve();
for (let i = 0; i < 6; i++) chain2 = chain2.then(() => { tick2++; });
drainJobQueue();
assertEq(safeTick, baseTick + 1,
"promise-with-promise via safe-resolve is +1 tick");
}