Les*_*els 8 javascript asynchronous async-await reactjs
我使用 Javascript 已经有几年了,根据我目前对事件循环的了解,我正在努力理解为什么React 文档中的这个测试方法有效。有人能够准确地分解每一步发生的情况吗?对我来说,这在测试中起作用似乎很神奇:
await act(async () => {
render(<User id="123" />, container);
});
// expect something
Run Code Online (Sandbox Code Playgroud)
该组件如下所示(复制以防链接被弃用):
function User(props) {
const [user, setUser] = useState(null);
async function fetchUserData(id) {
const response = await fetch("/" + id);
setUser(await response.json());
}
useEffect(() => {
fetchUserData(props.id);
}, [props.id]);
if (!user) {
return "loading...";
}
return (
<details>
<summary>{user.name}</summary>
<strong>{user.age}</strong> years old
<br />
lives in {user.address}
</details>
);
}
Run Code Online (Sandbox Code Playgroud)
渲染上没有隐式或显式返回发生,那么 act 如何知道等待组件中发生的异步内容(获取等)?
对我来说,这更有意义:
await act(async () => render(<User id="123" />, container));
Run Code Online (Sandbox Code Playgroud)
或(这是同一件事):
await act(async () => {
return render(<User id="123" />, container);
});
Run Code Online (Sandbox Code Playgroud)
甚至:
await act(render(<User id="123" />, container));
Run Code Online (Sandbox Code Playgroud)
但这似乎不是人们使用它的方式,或者它的用途,所以我有点迷失。我见过同样的酶例子mount。
我不想创建一个脆弱的测试,所以我真的很想理解这一点。
它是否与异步回调有关,即是否最后将某些内容附加到事件循环,从而使等待等待渲染内的所有内容在解析之前发生?
我在这里画了一片空白,在反应文档丛林中挣扎,因为每个人都使用这种模式,但没有人真正解释它为什么或如何工作。
我在这里先向您的帮助表示感谢!
当仔细查看源代码时react-dom,react-dom/test-utils似乎使整个事情起作用的是在recursivelyFlushAsyncActWork中的第一个效果刷新之后发生的setImmediate 调用。
act选择使用它似乎recursivelyFlushAsyncActWork只是因为回调具有“thenable”的签名,即 Promise。你可以在这里看到这个。
这应该意味着发生的事情(简化)是这样的:
useEffect被刷新(放入fetch事件循环)。setImmediate“确保”我们的模拟承诺/获取得到解决。setImmediate(由 调用enqueueTask)发生的,使状态更改出现在 DOM 中。resolve和我们的act决心。在看起来有点像这样的代码中(除了这是react-dom从我的React项目的node_modules的旧版本中获取的,现在flushWorkAndMicroTasks似乎被称为recursivelyFlushAsyncActWork):
function flushWorkAndMicroTasks(resolve) {
try {
flushWork(); // <- First effect flush (fetch will be invoked by the useEffect?)
enqueueTask(function () { // <- setImmediate is called in here (finishes the fetch)
if (flushWork()) { // <- Flush one more time and the next loop this will be false
flushWorkAndMicroTasks(resolve);
} else {
resolve(); // <- resolve is called when flushWork has nothing left to flush.
}
});
} catch (err) {
resolve(err);
}
}
Run Code Online (Sandbox Code Playgroud)
除非我弄错了,否则这应该意味着await act(async () => { render(...); });“仅”等待一个事件循环,除非最新的刷新添加了新任务。也就是说,如果中间存在刷新,则可以递归地添加承诺,并且诸如承诺链之类的微任务也可能会在第一个循环期间解决(因为它们在技术上是“阻塞”的(源代码))。
这意味着,如果您在模拟或 React 代码中添加计时器或其他内容,自然可能需要多个循环来解析,则不会等待它,因为在解析之前不会捕获“after”事件,因为 then 侦听器未附加/返回到外部承诺(如果我错了,请纠正我!)。