Event loop¶
Event loop (или цикл событий) позволяет выполнять однопоточному Node.js неблокирующие операции ввода/вывода, передавая их выполнение ядру системы, когда это возможно.
Стадии event loop¶
Кратко работу event loop Node.js можно описать так: операция передается на выполнения ядру системы, после завершения Node.js получает уведомление в том, что определенная для операции callback-функция может быть добавлена в очередь выполнения.
Инициализация event loop происходит в момент запуска сервера Node.js, и с этого момента он начинает свою работу, которую можно разделить на несколько этапов:
timers : выполнение callback-функций, зарегистрированных функциями setTimeout()
и setInterval()
;
pending callbacks : вызов callback-функций операций ввода/вывода, выполнение которых было отложено на предыдущей стадии цикла событий;
idle, prepare : выполнение внутренних действий, необходимых самому event loop;
poll : выполнение callback-функций завершенных асинхронных операций и управление фазой timers;
check : выполнение callback-функций, зарегистрированных функцией setImmediate()
;
close callbacks : обработка внезапно завершающихся действий.
На стадии timers выполняются зарегистрированные таймерами функции, причем переход на стадию контролируется стадией poll. Из-за блокировки стадией poll цикла событий, таймеры могут выполняться с некоторой задержкой, т. е. через больший интервал времени, чем тот, который был задан. Рассмотрим ситуацию на примере.
1 2 3 4 5 6 7 8 |
|
В примере сперва вызывается функция setTimeout()
, которая должна выполнить переданную ей функцию через 25 миллисекунд. Затем сразу же делает запрос к удаленному API, занимающий 20 миллисекунд. В момент завершения запроса до вызова функции в таймере останется еще 5 миллисекунд, поэтому event loop начнет выполнять callback-функцию, определенную для обработки результата запроса, выполнение которой занимает 10 миллисекунд. Получается, что в предполагаемый момент времени выполнение таймера будет невозможным из-за занятости цикла событий и его вызов произойдет по окончанию работы callback-а запроса, а именно через 30 миллисекунд после определения самого таймера.
Результат работы кода.
1 2 |
|
При pending callbacks выполняются действия, отложенные на предыдущей итерации event loop. Например, это могут быть сообщения об ошибках, которые не были выведены ранее из-за попытки системы их исправить.
При переходе в фазу poll в первую очередь проверяется, сформировалась ли очередь из callback-функций выполненных асинхронных действий. Если очередь не пуста, то в синхронном порядке начинается выполнение всех функций, находящихся в очереди. Выполнение будет продолжаться до тех пор, пока очередь не опустеет или не будет достигнут лимит выполняемых за раз callback-функций.
Если очередь оказывается пустой, то проверяется наличие действий, заданных функцией setImmediate()
, и если таковые имеются - происходит переход на стадию check, в противном случае Node.js event loop проверит, есть ли таймеры для выполнения. Если таймеры имеются - произойдет переход на timers, если нет - event loop будет ждать добавления в очередь новых callback-ов и при их появлении сразу же начинать их выполнение.
Для недопущения длительной блокировки event loop, в Node.js имеется ограничение на количество выполняемых на стадии poll callback-функций.
На стадии close callbacks вызываются функции, зарегистрированные для действий, возникающих внезапно. например, событие close
или disconnect
для сокет соединения.
process.nextTick()¶
Node.js process.nextTick()
позволяет выполнять переданные ему callback-функции в текущий момент времени, вне зависимости от того, на какой стадии находится выполнение event loop. Выполнение самого event loop продолжится сразу после завершения всех переданных process.nextTick()
callback-ов.
1 2 |
|
Выполнение переданной process.nextTick()
callback-функции начинается сразу после завершения текущей итерации event loop.