@Fedooot01

Почему Event Loop пропускает первый task и сразу переходит к микротаскам?

Здравствуйте, пытаюсь разобраться в тонкостях эвентлупа в JavaScript'е.

Судя по этому докладу, примерный псевдокод эвентлупа выглядит вот так:
// EVENTLOOP
while (true) {
    task = taskQueue.pop()
    execute(task)

    while (microtaskQueue.hasTasks()) {
      doMicroTask()
    }

    if (isRepaintTIme()) {
        doAnimationTask(task)
        repaint()
    }
}


А так же, судя по плану выполнения визуализатору эвентлупа, сначала выполняются таски, потом микротаски и в конце рендер.

Судя по этому плану эвентлупа, код:
setTimeout(() => console.log('timeout'), 0);
Promise.resolve('promise resloved').then(res => console.log(res))

должен сначала вывести timeout так как очередь тасков пуста и мы добавили таск setTimeout, а потом промис promise resloved. Но выполняется, конечно, сначала промис, а потом таймаут. Как будто начальное выполнение всего этого кода уже и есть таск. Либо как будто первая итерация эвентлупа пропускает таски и начинает сразу с микротасков.

Даже в визуализаторе эвентлупа, в этом примере, выполняя этот код по шагам, при первом проходе заход в пункт 2: Run a Task почему-то пропускается. А вот если в setTimeout'е добавить создание микротаски, то во второй итерации эвентлупа пункт 2 не пропускается и в таску визуализатор заходит нормально.

Можно подумать, что это от того, что setTimeout выполняется не моментально, а с задержкой 4 мс, но если модифицировать код, чтобы setTimeout точно успел отработать, результат не изменится:
setTimeout(() => console.log('timeout'), 0);
// отбросим потенциальную задержку от не мгновенного исполнения setTimeout
for (let i = 0; i < 10e8; i++) {}
Promise.resolve('promise resloved').then(res => console.log(res))


Почему реальное поведение отличается от псевдокода и визуализатора для первой итерации эвентлупа?
  • Вопрос задан
  • 293 просмотра
Решения вопроса 2
Rsa97
@Rsa97
Для правильного вопроса надо знать половину ответа
Как будто начальное выполнение всего этого кода уже и есть таск.
Вы таки не поверите, но код, запускающийся при загрузке скрипта - это отдельный таск. Он помещается в стек тасков и выполняется, пока стек не опустеет. Затем выполняются все накопившиеся микротаски, затем из очереди тасков в стек перемещается следующий таск.
Ответ написан
@tehfreak
Промис выполняется первым, потому что это микротаск, а таймаут - макротаск.
Все микротаски завершаются до перехода к следующему макротаску.

Вот объяснение на русском языке: https://learn.javascript.ru/event-loop#makrozadach...
А вот спецификация эвентлупа если нужны подробности: https://html.spec.whatwg.org/multipage/webappapis....
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы