Очистить очередь обещаний? - PullRequest
0 голосов
/ 09 мая 2020

При написании тестов с использованием Jest асинхронный имитирующий запрос Ax ios вызывал вызов 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));

Насколько я понимаю, он создает новое обещание, которое немедленно выполняется. Как это гарантировать, что другой асинхронный код разрешится?

Теперь проблема решена. Во время тестового запуска сообщений консоли больше нет. Тест все равно проходит (не очень хорошо). Но это просто похоже на то, что я просто приземлился на другой стороне состояния гонки, вместо того, чтобы предотвратить состояние гонки в первую очередь.

1 Ответ

2 голосов
/ 10 мая 2020

Я начну с причины, по которой вам нужно это решение.

Причина в том, что у вас нет ссылки на asyn c задачу , и вам нужно, чтобы ваше тестовое утверждение запускалось после задачи asyn c. Если он у вас был, вы можете просто await на нем, это гарантирует, что ваше тестовое утверждение будет выполняться после задачи asyn c. (в вашем случае это ReactDOM.unmountComponentAtNode)

В вашем примере задача asyn c находится внутри componentDidMount, которая реагирует на вызовы, поэтому у вас нет ссылки на задачу asyn c.

Обычно простая реализация flushPromises будет выглядеть так:

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

Эта реализация основана на том факте, что операции asyn c в javascript основаны на задаче queue (javascript имеет несколько типов очередей для асинхронных c задач). В частности, обещания помещаются в очередь микрозадач. Каждый раз, когда операция asyn c (например, http-запрос) завершает выполнение задачи asyn c из очереди и выполняется.

setImmediate - это метод, который получает обратный вызов и сохраняет его на Immediates Queue и будет вызван в следующей итерации события l oop. Этот Immediates Queue проверяется после очереди микрозадач (которая содержит обещания).

Давайте проанализируем код, мы создаем новое обещание, которое ставится в очередь на end из Micro-task queue, это resolve будет вызываться на следующей итерации события l oop, что означает, что оно будет разрешено после всех обещаний, которые уже поставлены в очередь.

Надеюсь, это объяснение поможет.

Обратите внимание , что если внутри задачи asyn c вы поставите в очередь новое обещание, оно попадет в очередь в конце, это означает, что обещание это 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
...