刷新承诺队列?



在使用 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});
});
}

我找到了一篇显示解决方案的博客文章,但我不明白它如何做任何有用的事情:

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));

据我了解,它创造了一个新的承诺,可以立即解决。如何保证其他异步代码能够解析?

现在,它解决了这个问题。测试运行期间不再有控制台消息。测试仍然通过(这不是一个非常好的测试)。但这似乎只是我只是降落在竞争条件的另一边,而不是首先阻止竞争条件。

我将从您需要此解决方案的原因开始。

原因是您没有对异步任务的引用,并且您需要在异步任务之后运行测试断言。 如果你有它,你可以await它,这将确保你的测试断言将在异步任务之后运行。(在您的情况下,它是ReactDOM.unmountComponentAtNode)

在您的示例中,异步任务位于响应调用componentDidMount内部,因此您没有对异步任务的引用。

通常flushPromises的简单实现是:

const flushPromises = () => new Promise(resolve => setImmediate(resolve));

此实现基于以下事实:javascript 中的异步操作基于任务队列(javascript 有几种类型的异步任务队列)。 具体来说,承诺在微任务队列中排队。每次异步操作(如 http 请求)完成时,异步任务都会取消排队并执行。

setImmediate是一种获取回调并将其存储在Immediates Queue上的方法,并将在事件循环的下一次迭代中调用。 此Immediates Queue在微任务队列(包含承诺)之后进行检查。

让我们分析一下代码,我们正在创建一个新的承诺,它在Micro-task queue结束时排队,它resolve将在事件循环的下一次迭代中被调用,这意味着它将在所有已经排队的承诺之后被解析。

希望这个解释有所帮助。

请注意,如果在异步任务中您将对新承诺进行排队,它将在最后进入队列,这意味着flushPromises返回的承诺将不会在它之后运行。

一些帖子/视频了解更多信息:

  1. https://blog.insiderattack.net/promises-next-ticks-and-immediates-nodejs-event-loop-part-3-9226cbe7a6aa
  2. https://www.youtube.com/watch?v=8aGhZQkoFbQ
  3. https://www.youtube.com/watch?v=cCOL7MC4Pl0

最新更新