mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-11-05 08:13:06 +08:00
Evaluate every Promise.all entry before a dynamic entry's then lookup
A checked-dynamic entry's PromiseResolve reads its `then` property, which can be a getter. The tuple form resolved each entry as the array was built, so that getter ran before later entries evaluated; Node evaluates the whole array first. With a checked-dynamic entry, every entry now evaluates into a hidden local before the entries resolve.
This commit is contained in:
@@ -72,7 +72,28 @@ function lowerTuplePromiseAll(
|
||||
const voidPromise: IrType & { kind: "promise" } = { kind: "promise", inner: VOID };
|
||||
const entriesT = arrayOf(voidPromise);
|
||||
const dynPromise: IrType = { kind: "promise", inner: DYN };
|
||||
const entries = elems.map((e): IrExpr => {
|
||||
// A checked-dynamic entry's PromiseResolve can run a `then` getter, which
|
||||
// Node runs after the whole array literal evaluated. With such an entry,
|
||||
// every entry evaluates into a hidden local first.
|
||||
const staged: IrStmt[] = [];
|
||||
const values = !elems.some((e) => e.type.kind === "dyn")
|
||||
? elems
|
||||
: elems.map((e): IrExpr => {
|
||||
if (e.kind === "unitLit") return e;
|
||||
if (isUnitType(e.type)) {
|
||||
staged.push({ kind: "exprStmt", expr: e, loc });
|
||||
return {
|
||||
kind: "unitLit",
|
||||
unit: e.type.kind === "nullT" ? "null" : "undefined",
|
||||
type: e.type,
|
||||
loc,
|
||||
};
|
||||
}
|
||||
const local = lowerer.declareHiddenLocal("%allValue", e.type);
|
||||
staged.push({ kind: "varDecl", localId: local.id, init: e, loc });
|
||||
return varRef(local.id, e.type, loc);
|
||||
});
|
||||
const entries = values.map((e): IrExpr => {
|
||||
let promise: IrExpr;
|
||||
if (e.type.kind === "promise") promise = e;
|
||||
else if (e.type.kind === "dyn")
|
||||
@@ -164,7 +185,7 @@ function lowerTuplePromiseAll(
|
||||
loc,
|
||||
});
|
||||
}
|
||||
return {
|
||||
const all: IrExpr = {
|
||||
kind: "libCall",
|
||||
fn: "promise.allTuple",
|
||||
args: [
|
||||
@@ -174,6 +195,9 @@ function lowerTuplePromiseAll(
|
||||
type: resultT,
|
||||
loc,
|
||||
};
|
||||
return staged.length === 0
|
||||
? all
|
||||
: { kind: "seqExpr", stmts: staged, result: all, type: resultT, loc };
|
||||
}
|
||||
|
||||
/** `Promise.race([...])` on THE Promise global: the entries lower
|
||||
|
||||
@@ -26,6 +26,16 @@ async function text(): Promise<string> {
|
||||
async function both(): Promise<[number, string]> {
|
||||
return Promise.all([num(), text()]);
|
||||
}
|
||||
const thenable: any = {
|
||||
get then() {
|
||||
log.push("then getter");
|
||||
return (resolve: (v: number) => void) => resolve(1);
|
||||
},
|
||||
};
|
||||
function evaluated(): Promise<string> {
|
||||
log.push("second entry evaluated");
|
||||
return Promise.resolve("s");
|
||||
}
|
||||
interface Item {
|
||||
id: number;
|
||||
}
|
||||
@@ -49,6 +59,9 @@ async function main(): Promise<void> {
|
||||
log.push(`returned ${h}${i}`);
|
||||
const mixed = await Promise.all([later("x", 3, "x"), 5, later("y", 1, true), undefined]);
|
||||
log.push(`mixed ${mixed[0]} ${mixed[1]} ${mixed[2]} ${mixed[3]}`);
|
||||
// A dynamic entry's `then` getter runs after the whole array evaluated.
|
||||
const [fromThenable, second] = await Promise.all([thenable, evaluated()]);
|
||||
log.push(`thenable ${fromThenable} ${second}`);
|
||||
const raw: any = JSON.parse('{"value":41}');
|
||||
const [dynamic, counted] = await Promise.all([raw.value, later("z", 2, 2)]);
|
||||
log.push(`dynamic ${dynamic + counted}`);
|
||||
|
||||
Reference in New Issue
Block a user