Как ведет себя цикл событий JS, когда разрешение Promise зависит от setTimeout? - PullRequest
0 голосов
/ 30 октября 2019
console.log('1')
setTimeout(() => {
    console.log('2')
}, 0)
function three() {
    return new Promise(resolve => {
        setTimeout(() => {
            return new Promise(resolve => resolve('3'))
        },0)
    })  
}
three().then(result => console.log(result))
console.log('4')

Этот фрагмент кода выводит 1 4 2

Это поведение, которое я ожидал, основываясь на моем понимании цикла обработки событий javascript и модели параллелизма. Но это оставляет меня с некоторыми затяжными вопросами.

Прежде чем перейти к этим вопросам, я сначала нарушу свое понимание этого фрагмента кода.

Почему код выводит 1

объяснение не требуется

Почему код выводит 4

обратный вызов, который выводит 2, загружается в очередь событий (она же очередь макрокоманд) через 0 мс, но не выполняется, пока не будет очищен основной стек вызовов.

, даже если three было обещанием, которое было немедленно разрешено, его код загружается в очередь заданий (она же очередь микрозадач) и не будет выполняться, пока не будет очищен основной стек вызовов (независимо от содержимого). очереди событий)

Почему код выводит 2

после console.log(4) основной стек вызовов пуст и javascript ищет следующий обратный вызов для загрузки в основной стек,Можно с уверенностью предположить, что в этот момент некоторые «рабочие потоки» уже поместили функцию обратного вызова, которая выводит 2, в очередь задач макроса. Это загружается в стек и выводится 2.

Почему код НЕ выводит 3

Это немного размыто для меня. Функция three возвращает обещание, которое then в главном потоке. Функции обратного вызова, пропущенные через then, загружаются в очередь для микрозадач и выполняются перед следующей задачей в очереди для макрозадач. Таким образом, хотя вы можете подумать, что он будет запущен до обратного вызова, регистрирующего 2, на самом деле его вообще невозможно запустить. Это связано с тем, что Promise разрешается только с помощью функции обратного вызова его setTimeout, и эта функция обратного вызова (из-за setTimeout) будет работать только в том случае, если основной поток выполнения (тот же поток, который ожидает разрешения, обещает) пуст.

Почему это беспокоит меня

Я пытаюсь построить полную теоретическую мысленную модель того, как javascript управляет параллелизмом. Одна из недостающих частей в этой модели - это связь между сетевыми запросами, обещаниями и циклом событий. Возьмем приведенный выше фрагмент кода и предположим, что я заменил setTimeout three на какой-то сетевой запрос (очень распространенная вещь в асинхронной веб-разработке). Предполагая, что сетевой запрос ведет себя аналогично setTimeout, в том случае, когда «рабочий поток» завершен, обратный вызов помещается в очередь задач макрокоманды, мне трудно понять, как этот обратный вызов даже выполняется. Но это то, что происходит буквально все время.

Может кто-нибудь помочь мне понять? Есть ли у меня пропущенные пробелы в моем текущем понимании параллелизма js? Я сделал неверное предположение? Имеет ли что-нибудь из этого смысл? лол

1 Ответ

1 голос
/ 30 октября 2019

Почему код НЕ выводит 3

В этом коде:

function three() {
    return new Promise(resolve => {
        setTimeout(() => {
            return new Promise(resolve => resolve('3'))
        },0)
    })  
}
three().then(result => console.log(result))

Вы никогда не разрешите первое обещание, которое создает three(). Так как это тот, который возвращается из three(), обработчик .then() в three().then(...) никогда не вызывается.

Вы разрешаете обещание, созданное внутри таймера, но возвращаете только это обещаниек обратному вызову таймера, который ничего не делает.

Если вы измените свой код на это:

function three() {
    return new Promise(resolve => {
        setTimeout(() => {
            resolve('3');
        },0)
    })  
}
three().then(result => console.log(result))

Тогда вы увидите 3 get output.

Такэто не имеет ничего общего с циклом событий или с тем, как он работает. Это связано с не разрешением обещания, которое возвращает three(), поэтому обработчик .then() для этого обещания никогда не вызывается.


Я пытаюсь построить полную теоретическую мысленную модельо том, как JavaScript обрабатывает параллелизм. Одна из недостающих частей в этой модели - это связь между сетевыми запросами, обещаниями и циклом событий. Возьмем приведенный выше фрагмент кода и предположим, что я заменил setTimeout из трех на какой-то сетевой запрос (очень распространенная вещь в асинхронной веб-разработке). Предполагая, что сетевой запрос ведет себя аналогично setTimeout, в том случае, когда «рабочий поток» завершен, обратный вызов помещается в очередь задач макрокоманды, мне трудно понять, как этот обратный вызов даже выполняется. Но это то, что происходит буквально все время.

Сетевые запросы, обещания и таймеры проходят через цикл обработки событий. Существуют очень сложные правила определения приоритетов нескольких событий в очереди по отношению друг к другу. * Обработчики .then() обычно имеют приоритет в первую очередь.

Думайте о интерпретаторе Javascript как об этой упрощенной последовательности.

 Get event from event queue
 If nothing in the event queue, sleep until something is in the event queue
 Run callback function associated with the event you pull from the event queue
 Run that callback function until it returns
     Note, it may not be completely done with its work because it may
     have started other asynchronous operations and set up its own
     callbacks or promises for those.  But, it has returned from the
     original callback that started it
 When that callback returns, go back to the first step above and get the next event

Помните, что сетевые запросы, обещания, таймеры и буквально ВСЕ асинхронные операции в узле. js проходит очередь событий таким образом.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...