diff --git a/packages/react-dom-bindings/src/server/ReactDOMLegacyServerStreamConfig.js b/packages/react-dom-bindings/src/server/ReactDOMLegacyServerStreamConfig.js index 5fa0c88d13181..4b940731b99b0 100644 --- a/packages/react-dom-bindings/src/server/ReactDOMLegacyServerStreamConfig.js +++ b/packages/react-dom-bindings/src/server/ReactDOMLegacyServerStreamConfig.js @@ -20,6 +20,14 @@ export function scheduleWork(callback: () => void) { callback(); } +export function scheduleMicrotask(callback: () => void) { + // While this defies the method name the legacy builds have special + // overrides that make work scheduling sync. At the moment scheduleMicrotask + // isn't used by any legacy APIs so this is somewhat academic but if they + // did in the future we'd probably want to have this be in sync with scheduleWork + callback(); +} + export function flushBuffered(destination: Destination) {} export function beginWriting(destination: Destination) {} diff --git a/packages/react-noop-renderer/src/ReactNoopFlightServer.js b/packages/react-noop-renderer/src/ReactNoopFlightServer.js index 983ae748e0ea7..cf6f24404c3ed 100644 --- a/packages/react-noop-renderer/src/ReactNoopFlightServer.js +++ b/packages/react-noop-renderer/src/ReactNoopFlightServer.js @@ -25,6 +25,9 @@ type Destination = Array; const textEncoder = new TextEncoder(); const ReactNoopFlightServer = ReactFlightServer({ + scheduleMicrotask(callback: () => void) { + callback(); + }, scheduleWork(callback: () => void) { callback(); }, diff --git a/packages/react-noop-renderer/src/ReactNoopServer.js b/packages/react-noop-renderer/src/ReactNoopServer.js index 7d739d3178836..4e2832e4f2bfe 100644 --- a/packages/react-noop-renderer/src/ReactNoopServer.js +++ b/packages/react-noop-renderer/src/ReactNoopServer.js @@ -74,6 +74,9 @@ function write(destination: Destination, buffer: Uint8Array): void { } const ReactNoopServer = ReactFizzServer({ + scheduleMicrotask(callback: () => void) { + callback(); + }, scheduleWork(callback: () => void) { callback(); }, diff --git a/packages/react-server/src/ReactFlightServer.js b/packages/react-server/src/ReactFlightServer.js index 306e4b3909093..33199490bd620 100644 --- a/packages/react-server/src/ReactFlightServer.js +++ b/packages/react-server/src/ReactFlightServer.js @@ -26,6 +26,7 @@ import {enableFlightReadableStream} from 'shared/ReactFeatureFlags'; import { scheduleWork, + scheduleMicrotask, flushBuffered, beginWriting, writeChunkAndReturn, @@ -1514,7 +1515,7 @@ function pingTask(request: Request, task: Task): void { pingedTasks.push(task); if (pingedTasks.length === 1) { request.flushScheduled = request.destination !== null; - scheduleWork(() => performWork(request)); + scheduleMicrotask(() => performWork(request)); } } diff --git a/packages/react-server/src/ReactServerStreamConfigBrowser.js b/packages/react-server/src/ReactServerStreamConfigBrowser.js index a1f8a33d43e10..2e68ca7117544 100644 --- a/packages/react-server/src/ReactServerStreamConfigBrowser.js +++ b/packages/react-server/src/ReactServerStreamConfigBrowser.js @@ -27,6 +27,21 @@ export function scheduleWork(callback: () => void) { channel.port2.postMessage(null); } +function handleErrorInNextTick(error: any) { + setTimeout(() => { + throw error; + }); +} + +const LocalPromise = Promise; + +export const scheduleMicrotask: (callback: () => void) => void = + typeof queueMicrotask === 'function' + ? queueMicrotask + : callback => { + LocalPromise.resolve(null).then(callback).catch(handleErrorInNextTick); + }; + export function flushBuffered(destination: Destination) { // WHATWG Streams do not yet have a way to flush the underlying // transform streams. https://github.com/whatwg/streams/issues/960 diff --git a/packages/react-server/src/ReactServerStreamConfigBun.js b/packages/react-server/src/ReactServerStreamConfigBun.js index 36c94570ec91c..81f86a50b7b25 100644 --- a/packages/react-server/src/ReactServerStreamConfigBun.js +++ b/packages/react-server/src/ReactServerStreamConfigBun.js @@ -25,6 +25,8 @@ export function scheduleWork(callback: () => void) { setTimeout(callback, 0); } +export const scheduleMicrotask = queueMicrotask; + export function flushBuffered(destination: Destination) { // Bun direct streams provide a flush function. // If we don't have any more data to send right now. diff --git a/packages/react-server/src/ReactServerStreamConfigEdge.js b/packages/react-server/src/ReactServerStreamConfigEdge.js index e77dc28284a18..22f165ded94c1 100644 --- a/packages/react-server/src/ReactServerStreamConfigEdge.js +++ b/packages/react-server/src/ReactServerStreamConfigEdge.js @@ -13,6 +13,21 @@ export type PrecomputedChunk = Uint8Array; export opaque type Chunk = Uint8Array; export type BinaryChunk = Uint8Array; +function handleErrorInNextTick(error: any) { + setTimeout(() => { + throw error; + }); +} + +const LocalPromise = Promise; + +export const scheduleMicrotask: (callback: () => void) => void = + typeof queueMicrotask === 'function' + ? queueMicrotask + : callback => { + LocalPromise.resolve(null).then(callback).catch(handleErrorInNextTick); + }; + export function scheduleWork(callback: () => void) { setTimeout(callback, 0); } diff --git a/packages/react-server/src/ReactServerStreamConfigNode.js b/packages/react-server/src/ReactServerStreamConfigNode.js index cbd366ab54ba3..773c998610df0 100644 --- a/packages/react-server/src/ReactServerStreamConfigNode.js +++ b/packages/react-server/src/ReactServerStreamConfigNode.js @@ -26,6 +26,8 @@ export function scheduleWork(callback: () => void) { setImmediate(callback); } +export const scheduleMicrotask = queueMicrotask; + export function flushBuffered(destination: Destination) { // If we don't have any more data to send right now. // Flush whatever is in the buffer to the wire. diff --git a/packages/react-server/src/forks/ReactServerStreamConfig.custom.js b/packages/react-server/src/forks/ReactServerStreamConfig.custom.js index 22cd6551c0b28..a9799cb7ba190 100644 --- a/packages/react-server/src/forks/ReactServerStreamConfig.custom.js +++ b/packages/react-server/src/forks/ReactServerStreamConfig.custom.js @@ -31,6 +31,7 @@ export opaque type Chunk = mixed; // eslint-disable-line no-undef export opaque type BinaryChunk = mixed; // eslint-disable-line no-undef export const scheduleWork = $$$config.scheduleWork; +export const scheduleMicrotask = $$$config.scheduleMicrotask; export const beginWriting = $$$config.beginWriting; export const writeChunk = $$$config.writeChunk; export const writeChunkAndReturn = $$$config.writeChunkAndReturn; diff --git a/packages/react-server/src/forks/ReactServerStreamConfig.dom-fb-experimental.js b/packages/react-server/src/forks/ReactServerStreamConfig.dom-fb-experimental.js index 40d97c71b58cb..e1fa4a0869cdd 100644 --- a/packages/react-server/src/forks/ReactServerStreamConfig.dom-fb-experimental.js +++ b/packages/react-server/src/forks/ReactServerStreamConfig.dom-fb-experimental.js @@ -42,6 +42,19 @@ export interface Destination { onError(error: mixed): void; } +function handleErrorInNextTick(error: any) { + setTimeout(() => { + throw error; + }); +} + +export const scheduleMicrotask: (callback: () => void) => void = + typeof queueMicrotask === 'function' + ? queueMicrotask + : callback => { + Promise.resolve(null).then(callback).catch(handleErrorInNextTick); + }; + export function scheduleWork(callback: () => void) { setTimeout(callback, 0); } diff --git a/packages/react-server/src/forks/ReactServerStreamConfig.dom-fb.js b/packages/react-server/src/forks/ReactServerStreamConfig.dom-fb.js index e15f6808673c3..12ed6ba59852e 100644 --- a/packages/react-server/src/forks/ReactServerStreamConfig.dom-fb.js +++ b/packages/react-server/src/forks/ReactServerStreamConfig.dom-fb.js @@ -9,6 +9,10 @@ export * from '../ReactServerStreamConfigFB'; +export function scheduleMicrotask(callback: () => void) { + // We don't schedule work in this model, and instead expect performWork to always be called repeatedly. +} + export function scheduleWork(callback: () => void) { // We don't schedule work in this model, and instead expect performWork to always be called repeatedly. }