DevGang
Авторизоваться

Node.js визуализация: promise, async/await and process.nextTick

В статье предполагается, что вы знакомы с event loop и его фазами. Promise спасли положение тысячам разработчиков JavaScript, но знаем ли мы, как они работают под капотом?

В свое время ад обратного вызова был нормой для обработки асинхронных операций. Простой JavaScript-скрипт для чтения файла и вставки его содержимого в базу данных мог бы быть похож на этот фрагмент.

Нам больше не нужно иметь дело с этой вложенной структурой из-за promise и async/await.

Демистификация promise

Определение MDN для promise - это «возможное завершение (или сбой) асинхронной операции».

Определение выглядит так, как будто мы имеем дело с какой-то формой непонятной магии, но, проще говоря, promise - это контейнер для будущей ценности.

promise ведет себя в определенной степени как браузер, содержащий либо желаемое содержимое страницы, либо сообщение об ошибке. Всякий раз, когда мы переходим по адресу, ваш веб-браузер может находиться в трех разных состояниях:

  • При извлечении данных может быть показана пустая страница или загрузочный счетчик.
  • Содержимое страницы успешно отображено.
  • Может появиться ошибка.

Promise может иметь три статуса:

  1. pending, когда, например, запрос не завершен;
  2. fulfilled, когда сетевой запрос будет завершен;
  3. rejected, когда асинхронный запрос завершится.

Практический пример статуса promise

Недавно созданное promise находится в состоянии pending.

Аналогично, операция ввода-вывода (I/O), такая как запрос к базе данных, находится в pending до тех пор, пока она не разрешится или не выдаст ошибку.

Мы можем прикрепить прослушиватель .then/.catch для обработки будущего значения promise. Как только вызывается функция resolve/reject, promise меняет свой статус с pending на fulfilled/rejected.

async/await

Современный JavaScript полностью основан на async/await, но эти два выражения являются альтернативным способом написания promise.

Мы можем переписать фрагмент базы данных с promise на async/await или наоборот.

Мы можем перепутать синтаксис и сбить с толку наших коллег (крайне не рекомендуется для удобства чтения и согласованности).

Promise под капотом

Promise, по-видимому, обрабатываются синхронно, но их выполнение откладывается до определенного момента в будущем.

Следующий сценарий показывает эту console.log запускаются перед обратным вызовом promise.

Под капотом двигатель V8 запоминает функцию обратного вызова и помещает ее в очередь микрозадач, когда promise resolve/reject.

Как только стек вызовов опустеет, V8 сможет обрабатывать микрозадачи.

Очередь микрозадач Promise и цикл событий

Очередь микрозадач и цикл событий ожидают, пока стек вызовов опустеет, прежде чем отправлять задачи на выполнение, но какая из них имеет более высокий приоритет?

Следующий сценарий показывает, что Node.js и его внутренние компоненты всегда будут пытаться исчерпать очередь микрозадач, прежде чем передавать управление обратно циклу событий и его фазам:

setTimeout и Promise.reject ставят в очередь макрозадачу на этапе таймера и микрозадачу.

Обратный вызов в очереди микрозадач выполняется перед фазой таймера цикла событий. Как только микрозадач не останется, цикл событий может возобновить свое выполнение.

Обратный вызов setTimeout вызывает setImmediate и запускает другую микрозадачу promise, которая будет выполнена в пустом стеке вызовов.

process.nextTick

process.nextTick также планирует микрозадачи, которые Node.js управляется в выделенной очереди и имеет более высокий приоритет (в CommonJS), чем очередь promise и фазы цикла событий.

В следующем сценарии, Node.js помещает обратный вызов в их очередь.

Как только стек вызовов опустеет, Node.js обрабатывает микрозадачи process.nextTick, за которыми следуют очередь promise и фазы цикла событий.

#JavaScript #NodeJS
Комментарии
Чтобы оставить комментарий, необходимо авторизоваться

Присоединяйся в тусовку

В этом месте могла бы быть ваша реклама

Разместить рекламу