эксперимент
Давайте сначала проведем эксперимент: давайте посмотрим, что выполняется быстрее, промис, который разрешается немедленно, или немедленный тайм-аут (тайм-аут 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) удаляется из очереди задачи.