В: Хорошо, давайте перейдем к Node.js, я помню, как в прошлый раз я говорил о «неблокирующем» и «управляемом событиями», что возбудило мое любопытство, но вылило на меня ведро холодной воды, эти два слова это немного высокого класса.
A: Не волнуйтесь, давайте сначала посмотрим на простую сцену:
Вы должны были использовать Renren.com, чтобы просматривать новости друзей, верно? Простой способ добиться этого — поддерживать длительный опрос между пользователем и сервером.
Но он отличается от обычного Ajax тем, что сервер не вернет информацию сразу, он будет ждать, пока информация должна быть возвращена, и тогда он вернет информацию (например, ваш друг прислал новый статус).
С точки зрения традиционных серверов (таких как Apache), каждый раз, когда новый пользователь подключается к вашему веб-сайту, ваш сервер должен открывать новое соединение, каждое соединение должно занимать поток, и большинство этих потоков простаивают (например, ожидание от друзей, чтобы опубликовать статус, проверка базы данных и т. д.), хотя они простаивают, но все равно занимают память, то есть, если пользователь достигает определенного масштаба, память сервера будет исчерпана и парализована .
Есть много решений, например использование пула потоков, но он все равно заблокирован.Если все потоки в пуле потоков заблокированы (медленная скорость сети, временно используется кем-то злонамеренно), то следующий запрос будет в очереди.
Node.js отличается от других: он использует «неблокирующую» и «управляемую событиями» модель. Вы можете думать о нем как о цикле событий, который работает вечно. Когда приходит новый запрос, Event Loop получает этот запрос, а затем передает его другим потокам, таким как запрос к базе данных, а затем ответ на обратный вызов, а затем получение других запросов, вместо того, чтобы ждать возврата результата базы данных.
Если база данных возвращает результат, сервер возвращает его клиенту, и цикл продолжается. ЭтоСобытие: сервер будет иметь соответствующую обработку (или прием запросов, или некоторые обратные вызовы) только тогда, когда что-то произойдет.
В: Значит, неблокирующий и управляемый событиями Node.js основан на этом цикле событий?
A: Да, проще говоря, цикл событий Node.js основан на libuv, а цикл событий браузера основан наспецификация html5Конкретная реализация, определенная в , остается на усмотрение производителя браузера.
В: Интересно, есть два вида циклов событий.
A: Для сравнения, они чем-то похожи:
В браузере это относительно просто, стоит отметить, что после каждой задачи будут выполняться задачи в текущей очереди микрозадачи:
Node.js немного сложнее, каждый Event Loop должен пройти шесть этапов, после каждого этапа выполняется nextTick, микрозадачи (разрешенные промисы и т.д.):
┌───────────────────────┐
┌─>│ timers │ <─── setTimeout/setInterval callback
│ └──────────┬────────────┘ ┌─────────────────────────┐
│ │ │ nextTick queue │
│ │ <─────────────── │ │
│ │ │ microTask queue │
│ ┌──────────┴────────────┐ └─────────────────────────┘
│ │ I/O callbacks │
│ └──────────┬────────────┘ ┌─────────────────────────┐
│ │ │ nextTick queue │
│ │ <─────────────── │ │
│ │ │ microTask queue │
│ ┌──────────┴────────────┐ └─────────────────────────┘
│ │ idle, prepare │ <─── 仅内部使用
│ └──────────┬────────────┘
│ │ ┌─────────────────────────┐
│ │ │ nextTick queue │
│ │ <──────────────── │ │
│ │ │ microTask queue │
│ │ └─────────────────────────┘
│ │ ┌─────────────────────────┐
│ ┌──────────┴────────────┐ │ incoming: │
│ │ poll │ <────┤ connections, │
│ └──────────┬────────────┘ │ data, etc │
│ │ └─────────────────────────┘
│ │ ┌─────────────────────────┐
│ │ │ nextTick queue │
│ │ <──────────────── │ │
│ │ │ microTask queue │
│ │ └─────────────────────────┘
│ ┌──────────┴────────────┐
│ │ check │ <─── setImmediate callback
│ └──────────┬────────────┘ ┌─────────────────────────┐
│ │ │ nextTick queue │
│ │ <─────────────── │ │
│ │ │ microTask queue │
│ ┌──────────┴────────────┐ └─────────────────────────┘
│ │ close callbacks │ <─── eg: socket.on("close",func)
│ └──────────┬────────────┘ ┌─────────────────────────┐
│ │ │ nextTick queue │
│ │ <─────────────── │ │
└─────────────┴ │ microTask queue │
└─────────────────────────┘
Давайте возьмем простой код и угадаем, что выводят браузер (Chrome) и Node.js соответственно:
console.log('start');
setTimeout(() => {
console.log('timer1');
Promise.resolve().then(() => {
console.log('promise1');
});
}, 0);
setTimeout(() => {
console.log('timer2');
Promise.resolve().then(() => {
console.log('promise2');
});
}, 0);
console.log('end');
В: Браузер (Chrome) должен выводить start, end, time1, promise1, time2, promise2 Что касается Node.js, я думаю, то же самое?
A: Давайте сначала проверим:
В браузере:
start
end
timer1
promise1
timer2
promise2
В Node.js:
start
end
timer1
timer2
promise1
promise2
Кажется, это отличается от того, что вы себе представляли, не волнуйтесь, просто посмотрите на анимацию, и вы поймете:
В браузере:
В Node.js:
В: Оказалось, что если каждыйsetTimeout callback
плюсprocess.nextTick
тогда чемPromise.then
Выполнить первым?
A: Да, помните, что я сказал выше, очередь nextTick и очередь micktasks будут выполняться после каждого этапа, и приоритет очереди nextTick выше, чем у очереди miktasks.
В: Я понимаю. Кстати, я помню, вы упомянули либув, что это такое? Разве там не сказано, что Node.js использует v8, какое это имеет отношение к v8?
А:...
Продолжение следует...
Примечание. После версии Node v11+ результат работы такой же, как и в браузере.