Очередь событий node.js не является единственной очередью. На самом деле это куча разных очередей и таких вещей, как process.nextTick()
и обработчики обещания .then()
не обрабатываются в одних и тех же очередях. Таким образом, события разных типов не обязательно являются FIFO.
Таким образом, если у вас есть несколько вещей, которые идут в очередь событий примерно в одно и то же время, и вы хотите, чтобы они обслуживались в определенном порядке, самый простой способ гарантировать этот порядок - написать свой код для принудительного выполнения требуемого порядка, не пытаться точно угадать, как будут последовательно упорядочены две вещи, которые попали в очередь одновременно.
Это правда, что две операции одного и того же типа, например две операции process.nextTick()
или две операции разрешенного обещания, будут обрабатываться в том порядке, в котором они были помещены в очередь событий. Но операции разных типов могут не обрабатываться в том порядке, в котором они были помещены в очередь событий, потому что разные типы событий обрабатываются в разное время в цикле, который цикл событий выполняет для всех различных типов событий.
Возможно, возможно полностью понять, как цикл событий в node.js работает для каждого типа событий, и точно предсказать, как два события, которые поступают в очередь событий примерно в одно и то же время, будут обрабатываться относительно друг друга, но это нелегко. Это еще более осложняется тем фактом, что это также зависит от того, где находится цикл обработки событий в его текущей обработке, когда новые события добавляются в очередь событий.
Как и в моем примере доставки в моих предыдущих комментариях, когда именно новая доставка будет обработана относительно других поставок, зависит от того, где находится драйвер доставки, когда новый заказ поступает в очередь. То же самое может быть верно для системы событий node.js. Если новое событие вставляется в то время, когда node.js обрабатывает событие таймера, оно может иметь относительный порядок относительно других типов событий, чем если бы это node.js обрабатывал событие завершения файлового ввода-вывода, когда оно было вставлено. Поэтому из-за этого существенного усложнения я не рекомендую пытаться предсказать порядок выполнения асинхронных событий различных типов, которые вставляются в очередь событий примерно в одно и то же время.
И, я должен добавить, что собственные обещания подключаются непосредственно к реализации цикла событий (как их собственный тип микро-задачи), поэтому реализация собственного обещания может вести себя по-другому в вашем исходном коде, чем реализация не собственного обещания. Снова причина не пытаться точно предсказать, как цикл событий будет планировать различные типы событий относительно друг друга.
Если порядок обработки важен для вашего кода, используйте код для принудительного выполнения определенного порядка обработки завершения.
В качестве примера того, как важно, что делает очередь событий, когда события вставляются в очередь событий, ваш код упрощен до следующего:
async function run(){
process.nextTick(()=>{
console.log(1);
});
await Promise.resolve().then(()=>{console.log(2)});
console.log(3);
process.nextTick(()=>{
console.log(4);
});
Promise.resolve().then(()=>{console.log(5)});
}
run();
Генерирует этот вывод:
1
2
3
5
4
Но просто измените, когда run()
называется Promise.resolve().then(run)
, и порядок внезапно меняется:
async function run(){
process.nextTick(()=>{
console.log(1);
});
await Promise.resolve().then(()=>{console.log(2)});
console.log(3);
process.nextTick(()=>{
console.log(4);
});
Promise.resolve().then(()=>{console.log(5)});
}
Promise.resolve().then(run);
Генерирует этот вывод, который сильно отличается:
2
3
5
1
4
Вы можете видеть, что когда код запускается из разрешенного обещания, тогда другие разрешенные обещания, которые происходят в этом коде, обрабатываются до событий .nextTick()
, чего не было, когда код был запущен из другой точки в обработка очереди событий. Это та часть, которая затрудняет прогнозирование системы очередей событий.
Итак, если вы пытаетесь гарантировать определенный порядок выполнения, вы должны либо использовать все события одного и того же типа, а затем они будут выполняться в порядке FIFO относительно друг друга, либо вы должны заставить свой код принудительно выполнять выполнение заказать вы хотите. Итак, если вы действительно хотели увидеть этот заказ:
1
2
3
4
5
Вы можете использовать все обещания, которые по существу соответствуют этому:
async function run(){
Promise.resolve().then(() => {
console.log(1);
})
Promise.resolve().then(() => {
console.log(2)
});
await Promise.resolve().then(()=>{});
console.log(3);
Promise.resolve().then(() => {
console.log(4)
});
Promise.resolve().then(()=>{console.log(5)});
}
run();
Или вы изменяете структуру своего кода, чтобы код всегда обрабатывал вещи в нужном порядке:
async function run(){
process.nextTick(async ()=>{
console.log(1);
await Promise.resolve().then(()=>{console.log(2)});
console.log(3);
process.nextTick(()=>{
console.log(4);
Promise.resolve().then(()=>{console.log(5)});
});
});
}
run();
Любой из этих двух последних сценариев сгенерирует вывод:
1
2
3
4
5