эксперимент
Давайте сначала проведем эксперимент: давайте посмотрим, что выполняется быстрее, промис, который разрешается немедленно, или немедленный тайм-аут (тайм-аут 0 мс)?
Promise.resolve(1).then(function resolve() {
console.log('Resolved!');
});
setTimeout(function timeout() {
console.log('Timed out!');
}, 0);
// logs 'Resolved!'
// logs 'Timed out!'
Promise.resolve(1)это статическая функция, которая возвращает обещание, которое немедленно разрешается.setTimeout(callback, 0)Выполнить обратный вызов с задержкой 0 миллисекунд.
Откройте выполнение и проверьте консоль. Вы увидите, что журнал печатается первым'Resolved!', а затем распечатал'Timeout completed!'. Немедленно разрешенные промисы обрабатываются быстрее, чем немедленные тайм-ауты.
Потому чтоPromise.resolve(true).then(...)существуетsetTimeout(..., 0)был вызван раньше, поэтому процесс обещания будет быстрее?
Затем мы изменим экспериментальные условия, сначала вызовемsetTimeout(...,0):
setTimeout(function timeout() {
console.log('Timed out!');
}, 0);
Promise.resolve(1).then(function resolve() {
console.log('Resolved!');
});
// logs 'Resolved!'
// logs 'Timed out!'
Выполнить и посмотреть консоль, результат тот же!
несмотря на то чтоsetTimeout(..., 0)существуетPromise.resolve(true).then(...)звонили раньше, но'Resolved!'еще в'Timed out!'ранее вывод.
Эксперименты показали, что обещания, которые разрешаются немедленно, разрешаются до немедленного истечения времени ожидания. так. . . почему это?
цикл событий
На вопросы об асинхронном JavaScript можно ответить, изучив цикл обработки событий. Начнем с рассмотрения того, как работает асинхронный JavaScript.

стек вызовов— это структура LIFO (последним пришел — первым обслужен), используемая для хранения контекста выполнения, созданного во время выполнения кода. Короче говоря, стек вызовов выполняет функцию.
Веб-API — это асинхронные операции (запросы на выборку, обещания, таймеры), и обратные вызовы ждут завершения работы здесь.
Очередь задач — это структура FIFO (первым поступил — первым обслужен), которая содержит обратные вызовы для асинхронных операций, готовых к выполнению. Например, тайм-аутsetTimeout()Обратный вызов (готовый к выполнению) попадает в очередь задач.
очередь заданийпредставляет собой структуру FIFO (первым поступил, первым обслужен), которая содержит обратные вызовы для промисов, готовых к выполнению. Например, разрешенные обратные вызовы разрешения или отклонения попадают в рабочую очередь.
наконец,цикл событийВсегда будет контролировать пустой стек вызовов. Если стек вызовов пуст, цикл обработки событий ищет рабочую очередь или очередь задач и удаляет из очереди обратные вызовы, готовые к выполнению, в стек вызовов.
Очередь работ и очередь задач
Давайте посмотрим на предыдущий эксперимент с точки зрения цикла событий. Я буду разбирать выполнение кода шаг за шагом.
- выполнение стека вызовов
setTimeout(..., 0)И «расписание» таймер.timeout()Обратные вызовы хранятся в веб-API:
setTimeout(function timeout() { console.log('Timed out!');}, 0);
Promise.resolve(1).then(function resolve() {
console.log('Resolved!');
});

- выполнение стека вызовов
Promise.resolve(true).then(resolve)И "расписание" обещание решить.resolved()Обратные вызовы хранятся в веб-API:
setTimeout(function timeout() {
console.log('Timed out!');
}, 0);
Promise.resolve(1).then(function resolve() { console.log('Resolved!');});
- Промис немедленно разрешается, и таймер немедленно отключается. В это время обратный вызов таймера
timeout()"поставлен в очередь" в очередь задач, обещание обратного вызоваresolve()Чтобы быть «в очереди» в рабочую очередь:
- А вот и забавная часть: приоритеты циклов событий опережают работу над задачами. Цикл событий выполняет обратный вызов обещания
resolve()Исключите очередь из рабочей очереди и поместите ее в стек вызовов, который затем выполняет обратный вызов обещания.resolve():
setTimeout(function timeout() {
console.log('Timed out!');
}, 0);
Promise.resolve(1).then(function resolve() {
console.log('Resolved!');});
'Resolved!'выводится на консоль.
- Наконец, цикл обработки событий вызывает таймер обратно.
timeout()Перемещено из очереди задач в стек вызовов. Затем стек вызовов выполняет обратный вызов таймераtimeout():
setTimeout(function timeout() {
console.log('Timed out!');}, 0);
Promise.resolve(1).then(function resolve() {
console.log('Resolved!');
});
'Timed out!'вывод в консоль.
В этот момент стек вызовов пуст. Выполнение скрипта завершено.
Суммировать
Почему обещания немедленного разрешения выполняются быстрее, чем немедленные таймеры?
Именно из-за «приоритета» цикла событий задачи в очереди задач (в которой хранится обратный вызов выполненного обещания) удаляются из очереди задач (в которой хранится тайм-аут).setTimeout()callback) удаляется из очереди задачи.