Глядя на цикл событий JS и очередь макромикрозадач из вопросов интервью

JavaScript

задний план

Приятного дня. Друг поделился со мной главным вопросом интервью:

async function async1(){
    console.log('async1 start')
    await async2()
    console.log('async1 end')
}
async function async2(){
    console.log('async2')
}
console.log('script start')
setTimeout(function(){
    console.log('setTimeout') 
},0)  
async1();
new Promise(function(resolve){
    console.log('promise1')
    resolve();
}).then(function(){
    console.log('promise2')
})
console.log('script end')

В этом разделе в основном изучается понимание порядка выполнения синхронных и асинхронных задач: setTimeout, promise, async/await. (рекомендуется сначала сделать это самостоятельно)

На тот момент, так как я мало что знал об асинхронности и ожидании, ответ был неверным :(), поэтому я его не записал, а потом перешел к статье, чтобы понять идею. Теперь запишите его ниже для дальнейшего использования.

Некоторые концепции опроса событий js

Здесь вам сначала нужно понять несколько понятий: синхронные задачи, асинхронные задачи, очереди задач, микрозадачи, макрозадачи

Синхронизировать задачу Относится к задачам, поставленным в очередь на выполнение в основном потоке, и только после выполнения предыдущей задачи может быть выполнена последняя задача;

асинхронная задача Относится к задаче, которая не входит в основной поток, но входит в «очередь задач» (очередь задач), ожидает завершения выполнения синхронной задачи и опрашивает выполнение задач в очереди асинхронных задач.

Макротаск — это макрозадача. Очередь макрозадач аналогична очереди задач, о которой мы часто говорим. Макрозадача — это асинхронная задача, распределяемая хост-средой. При опросе события всегда проверяется одна очередь задач на выполнение. «Очередь задач» является расширенной. Структура данных first-out, событие впереди, сначала читается основным потоком.

microtask — это микрозадача, которая распределяется движком js и всегда добавляется в конец текущей очереди задач для выполнения. Кроме того, при обработке микрозадач, если есть вновь добавленные микрозадачи, они также будут добавлены в конец очереди и выполнены. Обратите внимание на разницу с setTimeout(fn,0):

setTimeOut(fn(),0) Указывает, что задача выполняется в самое раннее свободное время, доступное для основного потока, то есть как можно раньше. Он добавляет событие в конец «очереди задач», поэтому оно не будет выполнено до тех пор, пока не будут обработаны как задача синхронизации, так и существующие события «очереди задач».

В заключение:

очередь задач, микрозадача, макрозадача

  • An event loop has one or more task queues.(task queue is macrotask queue)
  • Each event loop has a microtask queue.
  • task queue = macrotask queue != microtask queue
  • a task may be pushed into macrotask queue,or microtask queue
  • when a task is pushed into a queue(micro/macro),we mean preparing work is finished,so the task can be executed now.

Таким образом, мы можем получитьпорядок выполнения js:

Старт -> брать выполнение задачи в первую очередь задач (можно считать, что синхронная очередь задач является первой очередью задач) -> брать все задачи микрозадачи и выполнять их последовательно -> брать выполнение задачи в очередь следующая очередь задач -> снова взять микрозадачу Все задачи выполняются -> … этот цикл повторяется

Некоторые общие макро-задачи и микро-задачи:

макрозадача:

  • setTimeout
  • setInterval
  • setImmediate
  • requestAnimationFrame
  • I/O
  • UI rendering

microtask:

  • process.nextTick
  • Promises
  • Object.observe
  • MutationObserver

Promise, Async, Await — все асинхронные решения.

Promise — это конструктор, который генерирует экземпляр Promise при вызове. Функция обратного вызова, определенная в функции then, вызывается при изменении состояния промиса. Мы все знаем, что эта функция обратного вызова не будет выполнена немедленно, этомикрозадачибудет добавлено в конец текущей очереди задач и выполнено до того, как начнется выполнение следующего раунда задач.

Async/await появляется парами, отмеченная асинхронная функция вернет объект Promise, вы можете использовать метод then для добавления функции обратного вызова. Операторы, следующие за await, выполняются синхронно. Но приведенный ниже оператор await будет рассматриваться какмикрозадачиДобавить в конец асинхронного выполнения текущей очереди задач.

давайте посмотрим на ответ

Не могу вспомнить название! Продолжайте смотреть вниз, тепло подготовили тему, вверх листать не надо :)

async function async1(){
    console.log('async1 start')
    await async2()
    console.log('async1 end')
}
async function async2(){
    console.log('async2')
}
console.log('script start')
setTimeout(function(){
    console.log('setTimeout') 
},0)  
async1();
new Promise(function(resolve){
    console.log('promise1')
    resolve();
}).then(function(){
    console.log('promise2')
})
console.log('script end')

>= версия node10 - это результат: запуск сценария -> запуск async1 -> async2 -> обещание1 -> конец сценария -> обещание2 -> конец async1 -> setTimeout

запуск async1 -> async2 -> обещание1 -> конец сценария -> конец async1 -> обещание2 -> setTimeout

Согласно написанному выше порядку выполнения js можно получить правильный результат, но ответов в итоге два, почему два результата? Мы можем видеть разницу в порядке между async1 end и Promise2 в двух результатах.Я думаю, это связано с тем, что разные версии узла выполняют await по-разному, что приводит к разным моментам времени, когда код ниже await входит в очередь задач. Подробнее см.Как оптимизировать асинхронное программирование JavaScript в V8?Внутри "Глубокое понимание ожидания"

Простое понимание заключается в следующем:

async function f(){
  await p
  console.log(1);
}
//node.js8及即将推广的标准应该会解析成下面这样
function f(){
  Promise.resolve(p).then(()=>{
    console.log(1)
  })
}
//其余的版本应该会解析成下面的这样
function f(){
  new Promise(resolve=>{
    resolve(p)
  }).then(()=>{
    console.log(1)
  })
}

Основные различия между двумя вышеперечисленными:

  1. Когда параметр Promise.resolve является объектом промиса, объект промиса возвращается напрямую, а функция then выполняется сразу после изменения объекта промиса.
  2. Старая версия разрешения await повторно генерировала объект Promise. Хотя промис обязательно разрешится в p, сам процесс асинхронный, т.е.Теперь в очереди находится процесс разрешения нового обещания., поэтому обещание then не будет вызываться немедленно, но не будет вызываться до тех пор, пока текущая очередь не выполнит вышеупомянутый процесс разрешения, а затем не выполнит функцию then. (В следующем примере упражнения поясняется, что происходит, когда параметр resolve() является обещанием.)

Не беспокойтесь, что этот вопрос не решен, есть только одна истина.Согласно недавней резолюции TC39, await будет напрямую использовать Promise.resolve() с той же семантикой.

Наконец, мы анализируем возможный процесс выполнения этой темы (в среде Chrome) с последним разрешением:

  • Определить функции async1, async2. вывод 'запуск скрипта'
  • Добавьте функцию обратного вызова (задачу макроса) в setTimeout в следующий раунд очереди задач. Потому что этот код не выполняет никаких асинхронных операций до этого и время ожидания равно 0 с. Таким образом, функция обратного вызова будет немедленно помещена в начало следующего раунда очереди задач.
  • Выполнить асинхронный1. Мы знаем, что оператор перед тегом await в асинхронной функции и оператор после await выполняются синхронно. Поэтому здесь последовательно выводятся «async1 start» и «async2 start».
  • В это время выполнение следующего утверждения приостановлено, и в конце текущей очереди находится следующее утверждение.
  • Продолжите выполнение задачи синхронизации.
  • Выведите «Обещание1». Поместите функцию then в конец текущей очереди.
  • Затем выведите «конец сценария», обратите внимание, что в это время была выполнена только задача синхронизации, задача текущей очереди задач не выполнена, и добавлены две микрозадачи! -out, поэтому сначала выведите здесь «async1 end», а затем выведите «Promise2», тогда первый раунд очереди задач действительно завершен.
  • Затем выполните задачу следующего списка задач. Выполните асинхронную функцию внутри setTimeout. Выведите 'setTimeout'.

практиковать это

stackoverflowтема выше

let resolvePromise = new Promise(resolve => {
  let resolvedPromise = Promise.resolve()
  resolve(resolvedPromise)
})
resolvePromise.then(() => {
  console.log('resolvePromise resolved')
})
let resolvedPromiseThen = Promise.resolve().then(res => {
  console.log('promise1')
})
resolvedPromiseThen
  .then(() => {
    console.log('promise2')
  })
  .then(() => {
    console.log('promise3')
  })

Результат: обещание1 -> обещание2 -> разрешение обещания разрешено -> обещание3

Этот вопрос действительно сбивает с толку. Почему «resolvePromise разрешен» отображается только в третьей строке? Я провел ночь, обсуждая с моими соседями по комнате безрезультатно.

На самом деле, сложность этой темы заключается в том, как разрешить объект Promise и как js-движок будет с ним работать. Мы знаем, что когда параметр Promise.resolve() является объектом Promise, он напрямую возвращает объект Promise. Но когда аргумент resolve() является объектом Promise, ситуация меняется:

resolve(resolvedPromise)
//等同于:
Promise.resolve().then(() => resolvedPromise.then(resolve));

Итак, вот первый раз, когда он выполняется здесь:

  • Внутри первой функции() => resolvedPromise.then(resolve, reject)для микрозадач. будет помещен в конец текущего списка задач
  • Затем Promise1 помещается в конец списка задач.
  • Нет операции синхронизации для начала выполнения списка задач.В настоящее время, поскольку resolvePromise является разрешенным промисом, он напрямую выполняет функцию then, помещает функцию resole() в функцию then в конец текущей очереди, а затем выводит Promise1.
  • Поместите обещание2 в конце очереди. Выполнить резол ()
  • В это время, resolvePromise, наконец, становится объектом Promise в разрешенном состоянии и помещает «resolvePromise разрешил» в конец текущего списка задач. Выходное обещание2.
  • Поместите Promise3 в конец текущей очереди задач. Выходные данные resolvePromise разрешены. Наконец, выведите Promise3.

конец! Несколько фрагментов кода здесь более важны, объясняя, как js будет реализовывать эти новые функции.

Наконец, если есть какие-либо ошибки, пожалуйста, поправьте меня.

Ссылаться на:

Смотрите порядок выполнения асинхронных задач JavaScript через микрозадачи и макрозадачи

Подробное объяснение механизма работы JavaScript: снова поговорим о цикле событий

Результаты выполнения async/await в среде chrome и среде node несовместимы, решить?

What's the difference between resolve(thenable) and resolve('non-thenable-object')?