mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Support await expressions
Adds support for `await` expressions. We have primarily seen await used inside callbacks, not directly within component render logic, but because we construct HIR for lambdas it is helpful to be able to model await rather than require everyone to rewrite to use the Promise API. Note a subtlety: awaiting a promise is a mutative operation, so we a) model it as a Mutate effect and b) avoid DCE of await expressions since they may cause side effects. See the test cases for examples.
This commit is contained in:
@@ -1687,6 +1687,14 @@ function lowerExpression(
|
||||
};
|
||||
}
|
||||
}
|
||||
case "AwaitExpression": {
|
||||
let expr = exprPath as NodePath<t.AwaitExpression>;
|
||||
return {
|
||||
kind: "Await",
|
||||
value: lowerExpressionToTemporary(builder, expr.get("argument")),
|
||||
loc: exprLoc,
|
||||
};
|
||||
}
|
||||
case "TypeCastExpression": {
|
||||
let expr = exprPath as NodePath<t.TypeCastExpression>;
|
||||
return {
|
||||
|
||||
@@ -632,6 +632,11 @@ export type InstructionValue =
|
||||
quasis: Array<{ raw: string; cooked?: string }>;
|
||||
loc: SourceLocation;
|
||||
}
|
||||
| {
|
||||
kind: "Await";
|
||||
value: Place;
|
||||
loc: SourceLocation;
|
||||
}
|
||||
/**
|
||||
* Catch-all for statements such as type imports, nested class declarations, etc
|
||||
* which are not directly represented, but included for completeness and to allow
|
||||
|
||||
@@ -448,6 +448,10 @@ export function printInstructionValue(instrValue: ReactiveValue): string {
|
||||
value = `RegExp /${instrValue.pattern}/${instrValue.flags}`;
|
||||
break;
|
||||
}
|
||||
case "Await": {
|
||||
value = `Await ${printPlace(instrValue.value)}`;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
assertExhaustive(
|
||||
instrValue,
|
||||
|
||||
@@ -170,6 +170,10 @@ export function* eachInstructionValueOperand(
|
||||
yield* instrValue.subexprs;
|
||||
break;
|
||||
}
|
||||
case "Await": {
|
||||
yield instrValue.value;
|
||||
break;
|
||||
}
|
||||
case "RegExpLiteral":
|
||||
case "LoadGlobal":
|
||||
case "UnsupportedNode":
|
||||
@@ -423,6 +427,10 @@ export function mapInstructionOperands(
|
||||
instrValue.subexprs = instrValue.subexprs.map(fn);
|
||||
break;
|
||||
}
|
||||
case "Await": {
|
||||
instrValue.value = fn(instrValue.value);
|
||||
break;
|
||||
}
|
||||
case "RegExpLiteral":
|
||||
case "LoadGlobal":
|
||||
case "UnsupportedNode":
|
||||
|
||||
@@ -788,6 +788,17 @@ function inferBlock(
|
||||
state.define(lvalue, instrValue);
|
||||
continue;
|
||||
}
|
||||
case "Await": {
|
||||
state.initialize(instrValue, state.kind(instrValue.value));
|
||||
// Awaiting a value causes it to change state (go from unresolved to resolved or error)
|
||||
// It also means that any side-effects which would occur as part of the promise evaluation
|
||||
// will occur.
|
||||
state.reference(instrValue.value, Effect.Mutate);
|
||||
const lvalue = instr.lvalue;
|
||||
lvalue.effect = Effect.Mutate;
|
||||
state.alias(lvalue, instrValue.value);
|
||||
continue;
|
||||
}
|
||||
case "TypeCastExpression": {
|
||||
// A type cast expression has no effect at runtime, so it's equivalent to a raw
|
||||
// identifier:
|
||||
|
||||
@@ -190,6 +190,7 @@ function pruneableValue(value: InstructionValue, state: State): boolean {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case "Await":
|
||||
case "CallExpression":
|
||||
case "ComputedDelete":
|
||||
case "ComputedStore":
|
||||
|
||||
@@ -907,6 +907,10 @@ function codegenInstructionValue(
|
||||
value = t.regExpLiteral(instrValue.pattern, instrValue.flags);
|
||||
break;
|
||||
}
|
||||
case "Await": {
|
||||
value = t.awaitExpression(codegenPlace(cx, instrValue.value));
|
||||
break;
|
||||
}
|
||||
case "DeclareLocal":
|
||||
case "Destructure":
|
||||
case "StoreLocal": {
|
||||
|
||||
@@ -219,6 +219,7 @@ function mayAllocate(value: InstructionValue): boolean {
|
||||
case "Destructure": {
|
||||
return doesPatternContainSpreadElement(value.lvalue.pattern);
|
||||
}
|
||||
case "Await":
|
||||
case "DeclareLocal":
|
||||
case "StoreLocal":
|
||||
case "LoadGlobal":
|
||||
|
||||
@@ -424,6 +424,7 @@ function computeMemoizationInputs(
|
||||
rvalues: [],
|
||||
};
|
||||
}
|
||||
case "Await":
|
||||
case "TypeCastExpression": {
|
||||
return {
|
||||
// Indirection for the inner value, memoized if the value is
|
||||
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
async function Component(props) {
|
||||
const x = [];
|
||||
await populateData(props.id, x);
|
||||
return x;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
async function Component(props) {
|
||||
const $ = React.unstable_useMemoCache(2);
|
||||
const c_0 = $[0] !== props.id;
|
||||
let x;
|
||||
if (c_0) {
|
||||
x = [];
|
||||
await populateData(props.id, x);
|
||||
$[0] = props.id;
|
||||
$[1] = x;
|
||||
} else {
|
||||
x = $[1];
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
async function Component(props) {
|
||||
const x = [];
|
||||
await populateData(props.id, x);
|
||||
return x;
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
async function Component(props) {
|
||||
const user = await load(props.id);
|
||||
return <div>{user.name}</div>;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
async function Component(props) {
|
||||
const $ = React.unstable_useMemoCache(4);
|
||||
const c_0 = $[0] !== props.id;
|
||||
let t0;
|
||||
if (c_0) {
|
||||
t0 = await load(props.id);
|
||||
$[0] = props.id;
|
||||
$[1] = t0;
|
||||
} else {
|
||||
t0 = $[1];
|
||||
}
|
||||
const user = t0;
|
||||
const c_2 = $[2] !== user.name;
|
||||
let t1;
|
||||
if (c_2) {
|
||||
t1 = <div>{user.name}</div>;
|
||||
$[2] = user.name;
|
||||
$[3] = t1;
|
||||
} else {
|
||||
t1 = $[3];
|
||||
}
|
||||
return t1;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
async function Component(props) {
|
||||
const user = await load(props.id);
|
||||
return <div>{user.name}</div>;
|
||||
}
|
||||
Reference in New Issue
Block a user