Как работает Node.js: цикл событий и неблокирующий ввод-вывод
Единая модель выполнения, объясняющая, почему асинхронность, таймеры и потоки ведут себя именно так
- объяснить, что выполняется в единственном главном потоке, а что нет;
- проследить, как взаимодействуют стек вызовов, цикл событий и его очереди;
- назвать фазы цикла событий и то, что каждая из них обрабатывает;
- предсказать порядок
setTimeout,setImmediate,Promiseиprocess.nextTick; - распознавать блокирующий код и понимать, почему он замораживает весь сервер.
Один поток, один стек вызовов
Node выполняет ваш JavaScript в одном потоке с единственным стеком вызовов. Стек вызовов -- это список функций, которые сейчас выполняются: когда вы вызываете функцию, она кладётся на вершину, а когда она возвращается -- снимается. В каждый момент выполняется только функция на вершине.
Поскольку стек ровно один, две строки вашего JavaScript никогда не выполняются в один и тот же момент. Движок (V8) берёт один кусок работы, выполняет его до конца, затем берёт следующий. Именно поэтому вы никогда не увидите переменную, наполовину изменённую другим потоком: код JavaScript выполняется до завершения. Функцию не прерывает на середине какой-то другой коллбэк; этот другой коллбэк должен ждать, пока стек снова не опустеет.
Эта однопоточная модель -- предпосылка для всего остального в этом уроке. Если запомнить одну фразу, пусть это будет: коллбэк может начаться, только когда стек вызовов пуст. Вся работа цикла событий -- решать, какой коллбэк отправить на этот пустой стек следующим.
Аналогия. Представьте одного повара на кухне (поток). Повар может делать только одно действие за раз. Но умный повар не стоит и не смотрит на духовку, пока печётся торт -- он ставит таймер, начинает нарезать овощи и возвращается к торту, когда таймер прозвенит. Духовка, таймер и курьер -- это помощники вне повара. Цикл событий -- это привычка повара всегда проверять "что для меня сейчас готово?" перед началом следующей задачи.