Files
react/packages/shared/enqueueTask.js

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

49 lines
1.8 KiB
JavaScript
Raw Normal View History

await act(async () => ...) (#14853) This took a while, but I'm happy I went through it. Some key moments - recursively flushing effects, flushing microtasks on each async turn, and my team's uncompromising philosophy on code reuse. Really happy with this. I still want to expand test coverage, and I have some more small related todos, but this is good to land. On to the next one. Soundtrack to landing this - https://open.spotify.com/track/0MF8I8OUo8kytiOo8aSHYq?si=gSWqUheKQbiQDXzptCXHTg * hacked up act(async () => {...}) * move stuff around * merge changes * abstract .act warnings and stuff. all renderers. pass all tests. * move testutils.act back into testutils * move into scheduler, rename some bits * smaller bundle * a comment for why we don't do typeof === 'function' * fix test * pass tests - fire, prod * lose actContainerElement * tighter * write a test for TestRenderer it's an odd one, because not only does sync act not flush effects correctly, but the async one does (wut). verified it's fine with the dom version. * lint * rewrote to move flushing logic closer to the renderer the scheduler's `flushPassiveEffects` didn't work as expected for the test renderer, so I decided to go back to the hack (rendering a dumb container) This also makes reactdom not as heavy (by a few bytes, but still). * move it around so the delta isn't too bad * cleanups fix promise chaining propagate errors correctly test for thenable the 'right' way more tests! tidier! ponies! * Stray comment * recursively flush effects * fixed tests * lint, move noop.act into react-reconciler * microtasks when checking if called, s/called/calledLog, cleanup * pass fb lint we could have globally changed our eslint config to assume Promise is available, but that means we expect a promise polyfill on the page, and we don't yet. this code is triggered only in jest anyway, and we're fairly certain Promise will be available there. hence, the once-off disable for the check * shorter timers, fix a test, test for Promise * use global.Promise for existence check * flush microtasks * a version that works in browsers (that support postMessage) I also added a sanity fixture inside fixtures/dom/ mostly for me. * hoist flushEffectsAndMicroTasks * pull out tick logic from ReactFiberScheduler * fix await act (...sync) hanging - fix a hang when awaiting sync logic - a better async/await test for test renderer * feedback changes - use node's setImmediate if available - a warning if MessageChannel isn't available - rename some functions * pass lint/flow checks (without requiring a Promise polyfill/exclusion) * prettier the prettiest, even. * use globalPromise for the missed await warning * __DEV__ check for didWarnAboutMessageChannel * thenables and callbacks instead of promises, pass flow/lint * tinier. better. - pulled most bits out of FiberScheduler - actedUpdates uses callbacks now * pass build validation * augh prettier * golfing 7 more chars * Test that effects are not flushed without also flushing microtasks * export doesHavePendingPassiveEffects, nits * createAct() * dead code * missed in merge? * lose the preflushing bits * ugh prettier * removed `actedUpdates()`, created shared/actingUpdatesScopeDepth * rearrange imports so builds work, remove the hack versions of flushPassiveEffects * represent actingUpdatesScopeDepth as a tuple [number] * use a shared flag on React.__SECRET... * remove createAct, setup act for all relevant renderers * review feedback shared/enqueueTask import ReactSharedInternals from 'shared/ReactSharedInternals'; simpler act() internals ReactSharedInternals.ReactShouldWarnActingUpdates * move act() implementation into createReactNoop * warnIfNotCurrentlyActingUpdatesInDev condition check order
2019-04-02 22:33:31 +01:00
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
await act(async () => ...) (#14853) This took a while, but I'm happy I went through it. Some key moments - recursively flushing effects, flushing microtasks on each async turn, and my team's uncompromising philosophy on code reuse. Really happy with this. I still want to expand test coverage, and I have some more small related todos, but this is good to land. On to the next one. Soundtrack to landing this - https://open.spotify.com/track/0MF8I8OUo8kytiOo8aSHYq?si=gSWqUheKQbiQDXzptCXHTg * hacked up act(async () => {...}) * move stuff around * merge changes * abstract .act warnings and stuff. all renderers. pass all tests. * move testutils.act back into testutils * move into scheduler, rename some bits * smaller bundle * a comment for why we don't do typeof === 'function' * fix test * pass tests - fire, prod * lose actContainerElement * tighter * write a test for TestRenderer it's an odd one, because not only does sync act not flush effects correctly, but the async one does (wut). verified it's fine with the dom version. * lint * rewrote to move flushing logic closer to the renderer the scheduler's `flushPassiveEffects` didn't work as expected for the test renderer, so I decided to go back to the hack (rendering a dumb container) This also makes reactdom not as heavy (by a few bytes, but still). * move it around so the delta isn't too bad * cleanups fix promise chaining propagate errors correctly test for thenable the 'right' way more tests! tidier! ponies! * Stray comment * recursively flush effects * fixed tests * lint, move noop.act into react-reconciler * microtasks when checking if called, s/called/calledLog, cleanup * pass fb lint we could have globally changed our eslint config to assume Promise is available, but that means we expect a promise polyfill on the page, and we don't yet. this code is triggered only in jest anyway, and we're fairly certain Promise will be available there. hence, the once-off disable for the check * shorter timers, fix a test, test for Promise * use global.Promise for existence check * flush microtasks * a version that works in browsers (that support postMessage) I also added a sanity fixture inside fixtures/dom/ mostly for me. * hoist flushEffectsAndMicroTasks * pull out tick logic from ReactFiberScheduler * fix await act (...sync) hanging - fix a hang when awaiting sync logic - a better async/await test for test renderer * feedback changes - use node's setImmediate if available - a warning if MessageChannel isn't available - rename some functions * pass lint/flow checks (without requiring a Promise polyfill/exclusion) * prettier the prettiest, even. * use globalPromise for the missed await warning * __DEV__ check for didWarnAboutMessageChannel * thenables and callbacks instead of promises, pass flow/lint * tinier. better. - pulled most bits out of FiberScheduler - actedUpdates uses callbacks now * pass build validation * augh prettier * golfing 7 more chars * Test that effects are not flushed without also flushing microtasks * export doesHavePendingPassiveEffects, nits * createAct() * dead code * missed in merge? * lose the preflushing bits * ugh prettier * removed `actedUpdates()`, created shared/actingUpdatesScopeDepth * rearrange imports so builds work, remove the hack versions of flushPassiveEffects * represent actingUpdatesScopeDepth as a tuple [number] * use a shared flag on React.__SECRET... * remove createAct, setup act for all relevant renderers * review feedback shared/enqueueTask import ReactSharedInternals from 'shared/ReactSharedInternals'; simpler act() internals ReactSharedInternals.ReactShouldWarnActingUpdates * move act() implementation into createReactNoop * warnIfNotCurrentlyActingUpdatesInDev condition check order
2019-04-02 22:33:31 +01:00
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/
let didWarnAboutMessageChannel = false;
let enqueueTaskImpl = null;
2022-10-03 20:57:34 -04:00
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);
};
await act(async () => ...) (#14853) This took a while, but I'm happy I went through it. Some key moments - recursively flushing effects, flushing microtasks on each async turn, and my team's uncompromising philosophy on code reuse. Really happy with this. I still want to expand test coverage, and I have some more small related todos, but this is good to land. On to the next one. Soundtrack to landing this - https://open.spotify.com/track/0MF8I8OUo8kytiOo8aSHYq?si=gSWqUheKQbiQDXzptCXHTg * hacked up act(async () => {...}) * move stuff around * merge changes * abstract .act warnings and stuff. all renderers. pass all tests. * move testutils.act back into testutils * move into scheduler, rename some bits * smaller bundle * a comment for why we don't do typeof === 'function' * fix test * pass tests - fire, prod * lose actContainerElement * tighter * write a test for TestRenderer it's an odd one, because not only does sync act not flush effects correctly, but the async one does (wut). verified it's fine with the dom version. * lint * rewrote to move flushing logic closer to the renderer the scheduler's `flushPassiveEffects` didn't work as expected for the test renderer, so I decided to go back to the hack (rendering a dumb container) This also makes reactdom not as heavy (by a few bytes, but still). * move it around so the delta isn't too bad * cleanups fix promise chaining propagate errors correctly test for thenable the 'right' way more tests! tidier! ponies! * Stray comment * recursively flush effects * fixed tests * lint, move noop.act into react-reconciler * microtasks when checking if called, s/called/calledLog, cleanup * pass fb lint we could have globally changed our eslint config to assume Promise is available, but that means we expect a promise polyfill on the page, and we don't yet. this code is triggered only in jest anyway, and we're fairly certain Promise will be available there. hence, the once-off disable for the check * shorter timers, fix a test, test for Promise * use global.Promise for existence check * flush microtasks * a version that works in browsers (that support postMessage) I also added a sanity fixture inside fixtures/dom/ mostly for me. * hoist flushEffectsAndMicroTasks * pull out tick logic from ReactFiberScheduler * fix await act (...sync) hanging - fix a hang when awaiting sync logic - a better async/await test for test renderer * feedback changes - use node's setImmediate if available - a warning if MessageChannel isn't available - rename some functions * pass lint/flow checks (without requiring a Promise polyfill/exclusion) * prettier the prettiest, even. * use globalPromise for the missed await warning * __DEV__ check for didWarnAboutMessageChannel * thenables and callbacks instead of promises, pass flow/lint * tinier. better. - pulled most bits out of FiberScheduler - actedUpdates uses callbacks now * pass build validation * augh prettier * golfing 7 more chars * Test that effects are not flushed without also flushing microtasks * export doesHavePendingPassiveEffects, nits * createAct() * dead code * missed in merge? * lose the preflushing bits * ugh prettier * removed `actedUpdates()`, created shared/actingUpdatesScopeDepth * rearrange imports so builds work, remove the hack versions of flushPassiveEffects * represent actingUpdatesScopeDepth as a tuple [number] * use a shared flag on React.__SECRET... * remove createAct, setup act for all relevant renderers * review feedback shared/enqueueTask import ReactSharedInternals from 'shared/ReactSharedInternals'; simpler act() internals ReactSharedInternals.ReactShouldWarnActingUpdates * move act() implementation into createReactNoop * warnIfNotCurrentlyActingUpdatesInDev condition check order
2019-04-02 22:33:31 +01:00
}
}
return enqueueTaskImpl(task);
await act(async () => ...) (#14853) This took a while, but I'm happy I went through it. Some key moments - recursively flushing effects, flushing microtasks on each async turn, and my team's uncompromising philosophy on code reuse. Really happy with this. I still want to expand test coverage, and I have some more small related todos, but this is good to land. On to the next one. Soundtrack to landing this - https://open.spotify.com/track/0MF8I8OUo8kytiOo8aSHYq?si=gSWqUheKQbiQDXzptCXHTg * hacked up act(async () => {...}) * move stuff around * merge changes * abstract .act warnings and stuff. all renderers. pass all tests. * move testutils.act back into testutils * move into scheduler, rename some bits * smaller bundle * a comment for why we don't do typeof === 'function' * fix test * pass tests - fire, prod * lose actContainerElement * tighter * write a test for TestRenderer it's an odd one, because not only does sync act not flush effects correctly, but the async one does (wut). verified it's fine with the dom version. * lint * rewrote to move flushing logic closer to the renderer the scheduler's `flushPassiveEffects` didn't work as expected for the test renderer, so I decided to go back to the hack (rendering a dumb container) This also makes reactdom not as heavy (by a few bytes, but still). * move it around so the delta isn't too bad * cleanups fix promise chaining propagate errors correctly test for thenable the 'right' way more tests! tidier! ponies! * Stray comment * recursively flush effects * fixed tests * lint, move noop.act into react-reconciler * microtasks when checking if called, s/called/calledLog, cleanup * pass fb lint we could have globally changed our eslint config to assume Promise is available, but that means we expect a promise polyfill on the page, and we don't yet. this code is triggered only in jest anyway, and we're fairly certain Promise will be available there. hence, the once-off disable for the check * shorter timers, fix a test, test for Promise * use global.Promise for existence check * flush microtasks * a version that works in browsers (that support postMessage) I also added a sanity fixture inside fixtures/dom/ mostly for me. * hoist flushEffectsAndMicroTasks * pull out tick logic from ReactFiberScheduler * fix await act (...sync) hanging - fix a hang when awaiting sync logic - a better async/await test for test renderer * feedback changes - use node's setImmediate if available - a warning if MessageChannel isn't available - rename some functions * pass lint/flow checks (without requiring a Promise polyfill/exclusion) * prettier the prettiest, even. * use globalPromise for the missed await warning * __DEV__ check for didWarnAboutMessageChannel * thenables and callbacks instead of promises, pass flow/lint * tinier. better. - pulled most bits out of FiberScheduler - actedUpdates uses callbacks now * pass build validation * augh prettier * golfing 7 more chars * Test that effects are not flushed without also flushing microtasks * export doesHavePendingPassiveEffects, nits * createAct() * dead code * missed in merge? * lose the preflushing bits * ugh prettier * removed `actedUpdates()`, created shared/actingUpdatesScopeDepth * rearrange imports so builds work, remove the hack versions of flushPassiveEffects * represent actingUpdatesScopeDepth as a tuple [number] * use a shared flag on React.__SECRET... * remove createAct, setup act for all relevant renderers * review feedback shared/enqueueTask import ReactSharedInternals from 'shared/ReactSharedInternals'; simpler act() internals ReactSharedInternals.ReactShouldWarnActingUpdates * move act() implementation into createReactNoop * warnIfNotCurrentlyActingUpdatesInDev condition check order
2019-04-02 22:33:31 +01:00
}