Skip to content

Commit

Permalink
Allow Async Functions to be used in Server Components
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
sebmarkbage committed Oct 14, 2022
1 parent fd31724 commit 80f50fb
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -362,13 +362,8 @@ describe('ReactFlightDOM', () => {
reject(e);
};
});
function DelayedText({children}, data) {
if (promise) {
throw promise;
}
if (error) {
throw error;
}
async function DelayedText({children}) {
await promise;
return <Text>{children}</Text>;
}
return [DelayedText, _resolve, _reject];
Expand Down Expand Up @@ -469,7 +464,9 @@ describe('ReactFlightDOM', () => {
resolveName();
});
// Advance time enough to trigger a nested fallback.
jest.advanceTimersByTime(500);
await act(async () => {
jest.advanceTimersByTime(500);
});
expect(container.innerHTML).toBe(
'<div>:name::avatar:</div>' +
'<p>(loading sidebar)</p>' +
Expand All @@ -482,7 +479,8 @@ describe('ReactFlightDOM', () => {
const theError = new Error('Game over');
// Let's *fail* loading games.
await act(async () => {
rejectGames(theError);
await rejectGames(theError);
await 'the inner async function';
});
const expectedGamesValue = __DEV__
? '<p>Game over + a dev digest</p>'
Expand All @@ -499,7 +497,8 @@ describe('ReactFlightDOM', () => {

// We can now show the sidebar.
await act(async () => {
resolvePhotos();
await resolvePhotos();
await 'the inner async function';
});
expect(container.innerHTML).toBe(
'<div>:name::avatar:</div>' +
Expand All @@ -510,7 +509,8 @@ describe('ReactFlightDOM', () => {

// Show everything.
await act(async () => {
resolvePosts();
await resolvePosts();
await 'the inner async function';
});
expect(container.innerHTML).toBe(
'<div>:name::avatar:</div>' +
Expand Down
31 changes: 29 additions & 2 deletions packages/react-server/src/ReactFlightServer.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import type {
ServerContextJSONValue,
Wakeable,
} from 'shared/ReactTypes';
import type {LazyComponent} from 'react/src/ReactLazy';

import {
scheduleWork,
Expand Down Expand Up @@ -192,6 +193,25 @@ function createRootContext(

const POP = {};

function readWakeable(wakeable: Wakeable) {
if (wakeable.status === 'fulfilled') {
return wakeable.value;
} else if (wakeable.status === 'rejected') {
throw wakeable.reason;
}
throw wakeable;
}

function createLazyWrapperAroundWakeable(wakeable) {
trackSuspendedWakeable(wakeable);
const lazyType: LazyComponent<T, Wakeable<T>> = {
$$typeof: REACT_LAZY_TYPE,
_payload: wakeable,
_init: readWakeable,
};
return lazyType;
}

function attemptResolveElement(
type: any,
key: null | React$Key,
Expand All @@ -214,7 +234,15 @@ function attemptResolveElement(
}
// This is a server-side component.
prepareToUseHooksForComponent(prevThenableState);
return type(props);
const result = type(props);
if (
typeof result === 'object' &&
result !== null &&
typeof result.then === 'function'
) {
return createLazyWrapperAroundWakeable(result);
}
return result;
} else if (typeof type === 'string') {
// This is a host element. E.g. HTML.
return [REACT_ELEMENT_TYPE, type, key, props];
Expand Down Expand Up @@ -636,7 +664,6 @@ export function resolveModelToJSON(

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

0 comments on commit 80f50fb

Please sign in to comment.