刷新 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 queueend 排队,它的 resolve 将被调用事件循环的下一次迭代,这意味着它将在所有已经入队的承诺之后得到解决。

希望这个解释对您有所帮助。

注意 如果在异步任务中你要入队一个新的promise,它会在最后进入队列,这意味着flushPromises return之后不会运行。

一些帖子/视频以获取更多信息:

  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