Skip to content

Commit 80f50fb

Browse files
committed
Allow Async Functions to be used in Server Components
This is a temporary step until we allow Promises everywhere. Currently this serializes to a Lazy which can then be consumed in this same slot by the client.
1 parent fd31724 commit 80f50fb

File tree

2 files changed

+40
-13
lines changed

2 files changed

+40
-13
lines changed

packages/react-server-dom-webpack/src/__tests__/ReactFlightDOM-test.js

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -362,13 +362,8 @@ describe('ReactFlightDOM', () => {
362362
reject(e);
363363
};
364364
});
365-
function DelayedText({children}, data) {
366-
if (promise) {
367-
throw promise;
368-
}
369-
if (error) {
370-
throw error;
371-
}
365+
async function DelayedText({children}) {
366+
await promise;
372367
return <Text>{children}</Text>;
373368
}
374369
return [DelayedText, _resolve, _reject];
@@ -469,7 +464,9 @@ describe('ReactFlightDOM', () => {
469464
resolveName();
470465
});
471466
// Advance time enough to trigger a nested fallback.
472-
jest.advanceTimersByTime(500);
467+
await act(async () => {
468+
jest.advanceTimersByTime(500);
469+
});
473470
expect(container.innerHTML).toBe(
474471
'<div>:name::avatar:</div>' +
475472
'<p>(loading sidebar)</p>' +
@@ -482,7 +479,8 @@ describe('ReactFlightDOM', () => {
482479
const theError = new Error('Game over');
483480
// Let's *fail* loading games.
484481
await act(async () => {
485-
rejectGames(theError);
482+
await rejectGames(theError);
483+
await 'the inner async function';
486484
});
487485
const expectedGamesValue = __DEV__
488486
? '<p>Game over + a dev digest</p>'
@@ -499,7 +497,8 @@ describe('ReactFlightDOM', () => {
499497

500498
// We can now show the sidebar.
501499
await act(async () => {
502-
resolvePhotos();
500+
await resolvePhotos();
501+
await 'the inner async function';
503502
});
504503
expect(container.innerHTML).toBe(
505504
'<div>:name::avatar:</div>' +
@@ -510,7 +509,8 @@ describe('ReactFlightDOM', () => {
510509

511510
// Show everything.
512511
await act(async () => {
513-
resolvePosts();
512+
await resolvePosts();
513+
await 'the inner async function';
514514
});
515515
expect(container.innerHTML).toBe(
516516
'<div>:name::avatar:</div>' +

packages/react-server/src/ReactFlightServer.js

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import type {
2222
ServerContextJSONValue,
2323
Wakeable,
2424
} from 'shared/ReactTypes';
25+
import type {LazyComponent} from 'react/src/ReactLazy';
2526

2627
import {
2728
scheduleWork,
@@ -192,6 +193,25 @@ function createRootContext(
192193

193194
const POP = {};
194195

196+
function readWakeable(wakeable: Wakeable) {
197+
if (wakeable.status === 'fulfilled') {
198+
return wakeable.value;
199+
} else if (wakeable.status === 'rejected') {
200+
throw wakeable.reason;
201+
}
202+
throw wakeable;
203+
}
204+
205+
function createLazyWrapperAroundWakeable(wakeable) {
206+
trackSuspendedWakeable(wakeable);
207+
const lazyType: LazyComponent<T, Wakeable<T>> = {
208+
$$typeof: REACT_LAZY_TYPE,
209+
_payload: wakeable,
210+
_init: readWakeable,
211+
};
212+
return lazyType;
213+
}
214+
195215
function attemptResolveElement(
196216
type: any,
197217
key: null | React$Key,
@@ -214,7 +234,15 @@ function attemptResolveElement(
214234
}
215235
// This is a server-side component.
216236
prepareToUseHooksForComponent(prevThenableState);
217-
return type(props);
237+
const result = type(props);
238+
if (
239+
typeof result === 'object' &&
240+
result !== null &&
241+
typeof result.then === 'function'
242+
) {
243+
return createLazyWrapperAroundWakeable(result);
244+
}
245+
return result;
218246
} else if (typeof type === 'string') {
219247
// This is a host element. E.g. HTML.
220248
return [REACT_ELEMENT_TYPE, type, key, props];
@@ -636,7 +664,6 @@ export function resolveModelToJSON(
636664

637665
return serializeByRefID(newTask.id);
638666
} else {
639-
logRecoverableError(request, x);
640667
// Something errored. We'll still send everything we have up until this point.
641668
// We'll replace this element with a lazy reference that throws on the client
642669
// once it gets rendered.

0 commit comments

Comments
 (0)