刷新 Promise 队列?
Flushing a Promise queue?
使用 Jest 编写测试时,异步模拟 Axios 请求在安装组件后导致 setState()
调用。这导致测试期间控制台输出 运行.
it('renders without crashing', () => {
const mockAxios = new MockAdapter(axios);
mockAxios.onGet(logsURL).reply(200, [...axios_logs_simple]);
const div = document.createElement('div');
ReactDOM.render(<Logs />, div);
ReactDOM.unmountComponentAtNode(div);
});
并且在 <Logs>
组件中:
componentDidMount() {
axios.get(apiURL).then(response => {
this.setState({data: response.data});
});
}
我发现 blog post 显示了一个解决方案,但我不明白它是如何做的:
it('renders without crashing', async () => { //<-- async added here
const mockAxios = new MockAdapter(axios);
mockAxios.onGet(logsURL).reply(200, [...axios_logs_simple]);
const div = document.createElement('div');
ReactDOM.render(<Logs />, div);
await flushPromises(); //<-- and this line here.
ReactDOM.unmountComponentAtNode(div);
});
const flushPromises = () => new Promise(resolve => setImmediate(resolve));
据我了解,它创建了一个立即解决的新承诺。那如何保证其他异步代码将解析?
现在,问题解决了。测试期间没有更多的控制台消息 运行。测试仍然通过(这不是一个很好的测试)。但这就像我只是降落在竞争条件的另一边,而不是首先阻止竞争条件。
我将从您需要此解决方案的原因开始。
原因是您没有对异步任务的引用,您需要[=72]的测试断言=] 在 异步任务之后。
如果你有它,你可以只 await
它,这将确保你的测试断言将 运行 在 异步任务之后。 (在你的情况下是 ReactDOM.unmountComponentAtNode
)
在您的示例中,异步任务在 componentDidMount
内部,它会响应调用,因此您没有对异步任务的引用。
通常 flushPromises
的简单实现是:
const flushPromises = () => new Promise(resolve => setImmediate(resolve));
此实现基于 javascript 中的异步操作基于 任务队列 (javascript 有几种类型的异步任务队列).
具体来说,承诺在微任务队列中排队。每次异步操作(例如 http 请求)完成时,异步任务就会出列并执行。
setImmediate
是一种获取回调并将其存储在 Immediates Queue
上的方法,将在事件循环的下一次迭代中调用。
此 Immediates Queue
在 微任务队列(持有承诺)之后 检查。
让我们分析一下代码,我们正在创建一个新的承诺,它在 Micro-task queue
的 end 排队,它的 resolve
将被调用事件循环的下一次迭代,这意味着它将在所有已经入队的承诺之后得到解决。
希望这个解释对您有所帮助。
注意 如果在异步任务中你要入队一个新的promise,它会在最后进入队列,这意味着flushPromises
return之后不会运行。
一些帖子/视频以获取更多信息:
使用 Jest 编写测试时,异步模拟 Axios 请求在安装组件后导致 setState()
调用。这导致测试期间控制台输出 运行.
it('renders without crashing', () => {
const mockAxios = new MockAdapter(axios);
mockAxios.onGet(logsURL).reply(200, [...axios_logs_simple]);
const div = document.createElement('div');
ReactDOM.render(<Logs />, div);
ReactDOM.unmountComponentAtNode(div);
});
并且在 <Logs>
组件中:
componentDidMount() {
axios.get(apiURL).then(response => {
this.setState({data: response.data});
});
}
我发现 blog post 显示了一个解决方案,但我不明白它是如何做的:
it('renders without crashing', async () => { //<-- async added here
const mockAxios = new MockAdapter(axios);
mockAxios.onGet(logsURL).reply(200, [...axios_logs_simple]);
const div = document.createElement('div');
ReactDOM.render(<Logs />, div);
await flushPromises(); //<-- and this line here.
ReactDOM.unmountComponentAtNode(div);
});
const flushPromises = () => new Promise(resolve => setImmediate(resolve));
据我了解,它创建了一个立即解决的新承诺。那如何保证其他异步代码将解析?
现在,问题解决了。测试期间没有更多的控制台消息 运行。测试仍然通过(这不是一个很好的测试)。但这就像我只是降落在竞争条件的另一边,而不是首先阻止竞争条件。
我将从您需要此解决方案的原因开始。
原因是您没有对异步任务的引用,您需要[=72]的测试断言=] 在 异步任务之后。
如果你有它,你可以只 await
它,这将确保你的测试断言将 运行 在 异步任务之后。 (在你的情况下是 ReactDOM.unmountComponentAtNode
)
在您的示例中,异步任务在 componentDidMount
内部,它会响应调用,因此您没有对异步任务的引用。
通常 flushPromises
的简单实现是:
const flushPromises = () => new Promise(resolve => setImmediate(resolve));
此实现基于 javascript 中的异步操作基于 任务队列 (javascript 有几种类型的异步任务队列). 具体来说,承诺在微任务队列中排队。每次异步操作(例如 http 请求)完成时,异步任务就会出列并执行。
setImmediate
是一种获取回调并将其存储在 Immediates Queue
上的方法,将在事件循环的下一次迭代中调用。
此 Immediates Queue
在 微任务队列(持有承诺)之后 检查。
让我们分析一下代码,我们正在创建一个新的承诺,它在 Micro-task queue
的 end 排队,它的 resolve
将被调用事件循环的下一次迭代,这意味着它将在所有已经入队的承诺之后得到解决。
希望这个解释对您有所帮助。
注意 如果在异步任务中你要入队一个新的promise,它会在最后进入队列,这意味着flushPromises
return之后不会运行。
一些帖子/视频以获取更多信息: