Skip to content

Commit 8b1d407

Browse files
committed
[Flight][Fizz] schedule work async
While most builds of Flight and Fizz schedule work in new tasks some do execute work synchronously. While this is necessary for legacy APIs like renderToString for modern APIs there really isn't a great reason to do this synchronously. This change updates all non-legacy uses to be async using the best availalble scheduler at runtime
1 parent f55d172 commit 8b1d407

15 files changed

+380
-209
lines changed

packages/react-dom/src/__tests__/ReactClassComponentPropResolutionFizz-test.js

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,13 @@ describe('ReactClassComponentPropResolutionFizz', () => {
3737
document.body.removeChild(container);
3838
});
3939

40+
async function serverAct(scope) {
41+
jest.useRealTimers();
42+
await scope();
43+
await new Promise(r => setTimeout(r, 1));
44+
jest.useFakeTimers();
45+
}
46+
4047
async function readIntoContainer(stream) {
4148
const reader = stream.getReader();
4249
let result = '';
@@ -81,10 +88,12 @@ describe('ReactClassComponentPropResolutionFizz', () => {
8188

8289
// `ref` should never appear as a prop. `default` always should.
8390
const ref = React.createRef();
84-
const stream = await ReactDOMServer.renderToReadableStream(
85-
<Component text="Yay" ref={ref} />,
86-
);
87-
await readIntoContainer(stream);
91+
await serverAct(async () => {
92+
const stream = await ReactDOMServer.renderToReadableStream(
93+
<Component text="Yay" ref={ref} />,
94+
);
95+
await readIntoContainer(stream);
96+
});
8897
assertLog([
8998
'constructor: text, default',
9099
'componentWillMount: text, default',

packages/react-dom/src/__tests__/ReactDOMFizzDeferredValue-test.js

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,13 @@ describe('ReactDOMFizzForm', () => {
5050
document.body.removeChild(container);
5151
});
5252

53+
async function serverAct(scope) {
54+
jest.useRealTimers();
55+
await scope();
56+
await new Promise(r => setTimeout(r, 1));
57+
jest.useFakeTimers();
58+
}
59+
5360
async function readIntoContainer(stream) {
5461
const reader = stream.getReader();
5562
let result = '';
@@ -76,8 +83,10 @@ describe('ReactDOMFizzForm', () => {
7683
return useDeferredValue('Final', 'Initial');
7784
}
7885

79-
const stream = await ReactDOMServer.renderToReadableStream(<App />);
80-
await readIntoContainer(stream);
86+
await serverAct(async () => {
87+
const stream = await ReactDOMServer.renderToReadableStream(<App />);
88+
await readIntoContainer(stream);
89+
});
8190
expect(container.textContent).toEqual('Initial');
8291

8392
// After hydration, it's updated to the final value
@@ -107,8 +116,10 @@ describe('ReactDOMFizzForm', () => {
107116
);
108117
}
109118

110-
const stream = await ReactDOMServer.renderToReadableStream(<App />);
111-
await readIntoContainer(stream);
119+
await serverAct(async () => {
120+
const stream = await ReactDOMServer.renderToReadableStream(<App />);
121+
await readIntoContainer(stream);
122+
});
112123
expect(container.textContent).toEqual('Loading...');
113124

114125
assertLog(['Loading...']);
@@ -153,9 +164,11 @@ describe('ReactDOMFizzForm', () => {
153164

154165
const cRef = React.createRef();
155166

156-
// The server renders using the "initial" value for B.
157-
const stream = await ReactDOMServer.renderToReadableStream(<App />);
158-
await readIntoContainer(stream);
167+
await serverAct(async () => {
168+
// The server renders using the "initial" value for B.
169+
const stream = await ReactDOMServer.renderToReadableStream(<App />);
170+
await readIntoContainer(stream);
171+
});
159172
assertLog(['A', 'B [Initial]', 'C']);
160173
expect(getVisibleChildren(container)).toEqual(
161174
<div>

packages/react-dom/src/__tests__/ReactDOMFizzForm-test.js

Lines changed: 72 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,13 @@ describe('ReactDOMFizzForm', () => {
4848
document.body.removeChild(container);
4949
});
5050

51+
async function serverAct(callback) {
52+
jest.useRealTimers();
53+
await callback();
54+
await new Promise(r => setTimeout(r, 1));
55+
jest.useFakeTimers();
56+
}
57+
5158
function submit(submitter) {
5259
const form = submitter.form || submitter;
5360
if (!submitter.form) {
@@ -96,8 +103,10 @@ describe('ReactDOMFizzForm', () => {
96103
);
97104
}
98105

99-
const stream = await ReactDOMServer.renderToReadableStream(<App />);
100-
await readIntoContainer(stream);
106+
await serverAct(async () => {
107+
const stream = await ReactDOMServer.renderToReadableStream(<App />);
108+
await readIntoContainer(stream);
109+
});
101110
await act(async () => {
102111
ReactDOMClient.hydrateRoot(container, <App />);
103112
});
@@ -143,8 +152,10 @@ describe('ReactDOMFizzForm', () => {
143152
);
144153
}
145154

146-
const stream = await ReactDOMServer.renderToReadableStream(<App />);
147-
await readIntoContainer(stream);
155+
await serverAct(async () => {
156+
const stream = await ReactDOMServer.renderToReadableStream(<App />);
157+
await readIntoContainer(stream);
158+
});
148159
await act(async () => {
149160
ReactDOMClient.hydrateRoot(container, <App />);
150161
});
@@ -175,8 +186,10 @@ describe('ReactDOMFizzForm', () => {
175186
);
176187
}
177188

178-
const stream = await ReactDOMServer.renderToReadableStream(<App />);
179-
await readIntoContainer(stream);
189+
await serverAct(async () => {
190+
const stream = await ReactDOMServer.renderToReadableStream(<App />);
191+
await readIntoContainer(stream);
192+
});
180193
await expect(async () => {
181194
await act(async () => {
182195
ReactDOMClient.hydrateRoot(container, <App isClient={true} />);
@@ -197,8 +210,10 @@ describe('ReactDOMFizzForm', () => {
197210
);
198211
}
199212

200-
const stream = await ReactDOMServer.renderToReadableStream(<App />);
201-
await readIntoContainer(stream);
213+
await serverAct(async () => {
214+
const stream = await ReactDOMServer.renderToReadableStream(<App />);
215+
await readIntoContainer(stream);
216+
});
202217
// This should ideally warn because only the client provides a function that doesn't line up.
203218
await act(async () => {
204219
ReactDOMClient.hydrateRoot(container, <App isClient={true} />);
@@ -231,8 +246,10 @@ describe('ReactDOMFizzForm', () => {
231246
);
232247
}
233248

234-
const stream = await ReactDOMServer.renderToReadableStream(<App />);
235-
await readIntoContainer(stream);
249+
await serverAct(async () => {
250+
const stream = await ReactDOMServer.renderToReadableStream(<App />);
251+
await readIntoContainer(stream);
252+
});
236253
let root;
237254
await act(async () => {
238255
root = ReactDOMClient.hydrateRoot(container, <App />);
@@ -278,8 +295,10 @@ describe('ReactDOMFizzForm', () => {
278295
);
279296
}
280297

281-
const stream = await ReactDOMServer.renderToReadableStream(<App />);
282-
await readIntoContainer(stream);
298+
await serverAct(async () => {
299+
const stream = await ReactDOMServer.renderToReadableStream(<App />);
300+
await readIntoContainer(stream);
301+
});
283302
let root;
284303
await act(async () => {
285304
root = ReactDOMClient.hydrateRoot(container, <App />);
@@ -333,13 +352,15 @@ describe('ReactDOMFizzForm', () => {
333352

334353
// Specifying the extra form fields are a DEV error, but we expect it
335354
// to eventually still be patched up after an update.
336-
await expect(async () => {
337-
const stream = await ReactDOMServer.renderToReadableStream(<App />);
338-
await readIntoContainer(stream);
339-
}).toErrorDev([
340-
'Cannot specify a encType or method for a form that specifies a function as the action.',
341-
'Cannot specify a formTarget for a button that specifies a function as a formAction.',
342-
]);
355+
await serverAct(async () => {
356+
await expect(async () => {
357+
const stream = await ReactDOMServer.renderToReadableStream(<App />);
358+
await readIntoContainer(stream);
359+
}).toErrorDev([
360+
'Cannot specify a encType or method for a form that specifies a function as the action.',
361+
'Cannot specify a formTarget for a button that specifies a function as a formAction.',
362+
]);
363+
});
343364
let root;
344365
await expect(async () => {
345366
await act(async () => {
@@ -379,8 +400,10 @@ describe('ReactDOMFizzForm', () => {
379400
return 'Pending: ' + pending;
380401
}
381402

382-
const stream = await ReactDOMServer.renderToReadableStream(<App />);
383-
await readIntoContainer(stream);
403+
await serverAct(async () => {
404+
const stream = await ReactDOMServer.renderToReadableStream(<App />);
405+
await readIntoContainer(stream);
406+
});
384407
expect(container.textContent).toBe('Pending: false');
385408

386409
await act(() => ReactDOMClient.hydrateRoot(container, <App />));
@@ -400,8 +423,10 @@ describe('ReactDOMFizzForm', () => {
400423
);
401424
}
402425

403-
const stream = await ReactDOMServer.renderToReadableStream(<App />);
404-
await readIntoContainer(stream);
426+
await serverAct(async () => {
427+
const stream = await ReactDOMServer.renderToReadableStream(<App />);
428+
await readIntoContainer(stream);
429+
});
405430

406431
// Dispatch an event before hydration
407432
submit(container.getElementsByTagName('form')[0]);
@@ -441,8 +466,10 @@ describe('ReactDOMFizzForm', () => {
441466
);
442467
}
443468

444-
const stream = await ReactDOMServer.renderToReadableStream(<App />);
445-
await readIntoContainer(stream);
469+
await serverAct(async () => {
470+
const stream = await ReactDOMServer.renderToReadableStream(<App />);
471+
await readIntoContainer(stream);
472+
});
446473

447474
submit(container.getElementsByTagName('input')[1]);
448475
submit(container.getElementsByTagName('button')[0]);
@@ -463,8 +490,10 @@ describe('ReactDOMFizzForm', () => {
463490
return optimisticState;
464491
}
465492

466-
const stream = await ReactDOMServer.renderToReadableStream(<App />);
467-
await readIntoContainer(stream);
493+
await serverAct(async () => {
494+
const stream = await ReactDOMServer.renderToReadableStream(<App />);
495+
await readIntoContainer(stream);
496+
});
468497
expect(container.textContent).toBe('hi');
469498

470499
await act(async () => {
@@ -484,8 +513,10 @@ describe('ReactDOMFizzForm', () => {
484513
return state;
485514
}
486515

487-
const stream = await ReactDOMServer.renderToReadableStream(<App />);
488-
await readIntoContainer(stream);
516+
await serverAct(async () => {
517+
const stream = await ReactDOMServer.renderToReadableStream(<App />);
518+
await readIntoContainer(stream);
519+
});
489520
expect(container.textContent).toBe('0');
490521

491522
await act(async () => {
@@ -521,8 +552,10 @@ describe('ReactDOMFizzForm', () => {
521552
);
522553
}
523554

524-
const stream = await ReactDOMServer.renderToReadableStream(<App />);
525-
await readIntoContainer(stream);
555+
await serverAct(async () => {
556+
const stream = await ReactDOMServer.renderToReadableStream(<App />);
557+
await readIntoContainer(stream);
558+
});
526559

527560
const form = container.firstChild;
528561
expect(form.getAttribute('action')).toBe('action');
@@ -581,8 +614,10 @@ describe('ReactDOMFizzForm', () => {
581614
);
582615
}
583616

584-
const stream = await ReactDOMServer.renderToReadableStream(<App />);
585-
await readIntoContainer(stream);
617+
await serverAct(async () => {
618+
const stream = await ReactDOMServer.renderToReadableStream(<App />);
619+
await readIntoContainer(stream);
620+
});
586621

587622
const input = container.getElementsByTagName('input')[1];
588623
const button = container.getElementsByTagName('button')[0];
@@ -651,8 +686,10 @@ describe('ReactDOMFizzForm', () => {
651686
);
652687
}
653688

654-
const stream = await ReactDOMServer.renderToReadableStream(<App />);
655-
await readIntoContainer(stream);
689+
await serverAct(async () => {
690+
const stream = await ReactDOMServer.renderToReadableStream(<App />);
691+
await readIntoContainer(stream);
692+
});
656693

657694
const barField = container.querySelector('[name=bar]');
658695

packages/react-dom/src/__tests__/ReactDOMFizzServerBrowser-test.js

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ let Suspense;
2121
describe('ReactDOMFizzServerBrowser', () => {
2222
beforeEach(() => {
2323
jest.resetModules();
24+
jest.useRealTimers();
2425
React = require('react');
2526
ReactDOMFizzServer = require('react-dom/server.browser');
2627
Suspense = React.Suspense;
@@ -103,13 +104,11 @@ describe('ReactDOMFizzServerBrowser', () => {
103104

104105
stream.allReady.then(() => (isComplete = true));
105106

106-
await jest.runAllTimers();
107107
expect(isComplete).toBe(false);
108108
// Resolve the loading.
109109
hasLoaded = true;
110110
await resolve();
111-
112-
await jest.runAllTimers();
111+
await new Promise(r => setTimeout(r, 0));
113112

114113
expect(isComplete).toBe(true);
115114

@@ -223,7 +222,7 @@ describe('ReactDOMFizzServerBrowser', () => {
223222
},
224223
);
225224

226-
await jest.runAllTimers();
225+
await new Promise(r => setTimeout(r, 0));
227226

228227
const theReason = new Error('aborted for reasons');
229228
controller.abort(theReason);
@@ -344,9 +343,8 @@ describe('ReactDOMFizzServerBrowser', () => {
344343
]);
345344

346345
hasLoaded = true;
347-
resolve();
348-
349-
await jest.runAllTimers();
346+
await resolve();
347+
await new Promise(r => setTimeout(r, 0));
350348

351349
expect(rendered).toBe(false);
352350
expect(isComplete).toBe(true);

0 commit comments

Comments
 (0)