Почему Promise быстрее, чем setTimeout()?

внешний интерфейс

эксперимент

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

наконец,цикл событийВсегда будет контролировать пустой стек вызовов. Если стек вызовов пуст, цикл обработки событий ищет рабочую очередь или очередь задач и удаляет из очереди обратные вызовы, готовые к выполнению, в стек вызовов.

Очередь работ и очередь задач

Давайте посмотрим на предыдущий эксперимент с точки зрения цикла событий. Я буду разбирать выполнение кода шаг за шагом.

  1. выполнение стека вызововsetTimeout(..., 0)И «расписание» таймер.timeout()Обратные вызовы хранятся в веб-API:
setTimeout(function timeout() {  console.log('Timed out!');}, 0);
Promise.resolve(1).then(function resolve() {
  console.log('Resolved!');
});

事件循环

  1. выполнение стека вызововPromise.resolve(true).then(resolve)И "расписание" обещание решить.resolved()Обратные вызовы хранятся в веб-API:
setTimeout(function timeout() {
  console.log('Timed out!');
}, 0);

Promise.resolve(1).then(function resolve() {  console.log('Resolved!');});

事件循环

  1. Промис немедленно разрешается, и таймер немедленно отключается. В это время обратный вызов таймераtimeout()"поставлен в очередь" в очередь задач, обещание обратного вызоваresolve()Чтобы быть «в очереди» в рабочую очередь:

事件循环

  1. А вот и забавная часть: приоритеты циклов событий опережают работу над задачами. Цикл событий выполняет обратный вызов обещанияresolve()Исключите очередь из рабочей очереди и поместите ее в стек вызовов, который затем выполняет обратный вызов обещания.resolve():
setTimeout(function timeout() {
  console.log('Timed out!');
}, 0);

Promise.resolve(1).then(function resolve() {
  console.log('Resolved!');});

'Resolved!'выводится на консоль.

Event Loop

  1. Наконец, цикл обработки событий вызывает таймер обратно.timeout()Перемещено из очереди задач в стек вызовов. Затем стек вызовов выполняет обратный вызов таймераtimeout():
setTimeout(function timeout() {
  console.log('Timed out!');}, 0);

Promise.resolve(1).then(function resolve() {
  console.log('Resolved!');
});

'Timed out!'вывод в консоль.

Event LoopВ этот момент стек вызовов пуст. Выполнение скрипта завершено.

Суммировать

Почему обещания немедленного разрешения выполняются быстрее, чем немедленные таймеры?

Именно из-за «приоритета» цикла событий задачи в очереди задач (в которой хранится обратный вызов выполненного обещания) удаляются из очереди задач (в которой хранится тайм-аут).setTimeout()callback) удаляется из очереди задачи.

Добро пожаловать в мой публичный аккаунт: фронтенд пионер