mirror of
https://github.com/zebrajr/react.git
synced 2026-01-15 12:15:22 +00:00
Internal test helpers: Use Node's MessageChannel to queue task (#26345)
To wait for the microtask queue to empty, our internal test helpers schedule an arbitrary task using `setImmediate`. It doesn't matter what kind of task it is, only that it's a separate task from the current one, because by the time it fires, the microtasks for the current event will have already been processed. The issue with `setImmediate` is that Jest mocks it. Which can lead to weird behavior. I've changed it to instead use a message event, via the MessageChannel implementation exposed by the `node:worker_threads` module. We should consider doing this in the public implementation of `act`, too.
This commit is contained in:
@@ -7,44 +7,10 @@
|
||||
* @flow
|
||||
*/
|
||||
|
||||
let didWarnAboutMessageChannel = false;
|
||||
let enqueueTaskImpl = null;
|
||||
const {MessageChannel} = require('node:worker_threads');
|
||||
|
||||
// Same as shared/enqeuueTask, but while that one used by the public
|
||||
// implementation of `act`, this is only used by our internal testing helpers.
|
||||
export default function enqueueTask(task: () => void): void {
|
||||
if (enqueueTaskImpl === null) {
|
||||
try {
|
||||
// read require off the module object to get around the bundlers.
|
||||
// we don't want them to detect a require and bundle a Node polyfill.
|
||||
const requireString = ('require' + Math.random()).slice(0, 7);
|
||||
const nodeRequire = module && module[requireString];
|
||||
// assuming we're in node, let's try to get node's
|
||||
// version of setImmediate, bypassing fake timers if any.
|
||||
enqueueTaskImpl = nodeRequire.call(module, 'timers').setImmediate;
|
||||
} catch (_err) {
|
||||
// we're in a browser
|
||||
// we can't use regular timers because they may still be faked
|
||||
// so we try MessageChannel+postMessage instead
|
||||
enqueueTaskImpl = function (callback: () => void) {
|
||||
if (__DEV__) {
|
||||
if (didWarnAboutMessageChannel === false) {
|
||||
didWarnAboutMessageChannel = true;
|
||||
if (typeof MessageChannel === 'undefined') {
|
||||
console['error'](
|
||||
'This browser does not have a MessageChannel implementation, ' +
|
||||
'so enqueuing tasks via await act(async () => ...) will fail. ' +
|
||||
'Please file an issue at https://github.com/facebook/react/issues ' +
|
||||
'if you encounter this warning.',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
const channel = new MessageChannel();
|
||||
channel.port1.onmessage = callback;
|
||||
channel.port2.postMessage(undefined);
|
||||
};
|
||||
}
|
||||
}
|
||||
return enqueueTaskImpl(task);
|
||||
const channel = new MessageChannel();
|
||||
channel.port1.onmessage = task;
|
||||
channel.port2.postMessage(undefined);
|
||||
}
|
||||
|
||||
@@ -498,7 +498,7 @@ Task 1 [Normal] │ █████████
|
||||
taskId++;
|
||||
const task = scheduleCallback(NormalPriority, () => {});
|
||||
cancelCallback(task);
|
||||
await waitForAll([]);
|
||||
Scheduler.unstable_flushAll();
|
||||
}
|
||||
|
||||
expect(console.error).toHaveBeenCalledTimes(1);
|
||||
|
||||
@@ -190,3 +190,10 @@ declare module 'async_hooks' {
|
||||
enterWith(store: T): void;
|
||||
}
|
||||
}
|
||||
|
||||
declare module 'node:worker_threads' {
|
||||
declare class MessageChannel {
|
||||
port1: MessagePort;
|
||||
port2: MessagePort;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user