Почему эта строка кода с 'await' запускает обработку очереди микрозадач? - PullRequest
9 голосов
/ 02 июля 2019

Следующие цитаты - мои основные ссылки для понимания обработки очереди микрозадач:

Микрозадачи (которые обещают использовать) обрабатываются, когда стек JS очищается.

- ДжейкАрчибальд

Это не имеет смысла для меня.

В одном цикле обработки событий будет ровно одна задача, обрабатываемая из очереди макросов(эта очередь просто называется очередью задач в спецификации WHATWG).После завершения этой макрозадачи все доступные микрозадачи будут обработаны, а именно в пределах одного цикла обработки.

- Переполнение стека

Теперь относительно строки9 (**) в следующем фрагменте:

При переходе через этот фрагмент с отладчиком стек выполнения не отображается пустым при обработке / выполнении этих .then( callback ) микрозадач.

обычные функции, такие как f2(), считаются заданием (или макрозадачей)?(когда он возвращается, это цикл обработки событий nextTick() и очередь микрозадач обрабатывается)

Почему выполняются микрозадачи, когда стек JS не пуст?

function f2() {
    let x = new Promise( (resolve, reject) => { resolve( () => {console.log('howdy')} ) })
    return x
}

async function f1(){
    let y = Promise.resolve().then(() => { console.log('yo1')})
    console.log('yo2')
	let r2awaited =  await f2() //** 'yo0' and 'yo1' log when the interpreter hits this line.
	return r2awaited
}

async function start(){
     let y = Promise.resolve().then(() => { console.log('yo0')})
	let xx = await f1()
	console.log('main call return:')
	console.log(xx)
}
start()

Редактировать: Еще один необычный вывод - когда вы добавляете xx() сразу после console.log(xx) в строке 17, стек полностью очищается перед выполнениеммикрозадачи.

Стек вызовов за 1 шаг до обработки очереди микрозадач:

The prior step

Затем следующий шаг.

The next step (after microtask queue processing)

Между этими двумя этапами была обработана очередь микротрасс.

Очищает ли стек вызовов [под колпаком] между этими шагами ^?

И затем создается ли новый стек вызовов в соответствии с необходимыми лексическими условиями для кода после await [expression]?

Редактировать: на момент публикации я не знал, что все, что находится ниже строки ----- (async) ----- в стеке вызовов отладчика chrome, является частью "поддельного стека"».

Этот «поддельный стек» представлен для асинхронной отладки в соответствии с синхронизацией отладки.

Только элементы над этой строкой ----- (async) ----- являются частью реального стека вызовов основного потока.

1 Ответ

8 голосов
/ 02 июля 2019

«Микрозадачи (которые обещают использовать) обрабатываются, когда стек JS очищается».Джейк Арчибальд (для меня это не имеет смысла)

"Стек вызовов" - это список вещей, которые выполняются в данный момент:

function foo() {
  debugger;
  console.log('foo');
}

function bar() {
  foo();
  debugger;
}

bar();

Когда мы нажимаем первыйоператор отладчика, сценарий все еще выполняется, как bar, как foo.Поскольку существуют отношения родитель-потомок, стек равен script > bar > foo.Когда мы нажимаем на второй оператор отладчика, foo завершил выполнение, поэтому он больше не находится в стеке.Стек составляет script > bar.

Очередь микротрассы обрабатывается до тех пор, пока она не станет пустой, когда стек станет пустым.

"Один цикл обработки событий будет иметь ровно одинЗадача, обрабатываемая из очереди макросов (эта очередь называется просто очередью заданий в спецификации WHATWG). После того, как эта макросзадача будет завершена, будут обработаны все доступные микрозадачи, а именно в рамках одного цикла обращения. "- stackoverflow

Edit : я продолжал читать "macrotask" выше как "microtask".На самом деле в браузере нет такой очереди как макрозадача, это просто очередь задач.

Несмотря на то, что после обработки задачи есть точка обработки микрозадачи, она действительно существует только для обработки спецификаций, которыеставить задачи в очередь для выполнения микрозадач без предварительного вызова JS.Большую часть времени очередь микрозадач освобождается, когда стек JS очищается.

При переходе через этот фрагмент с отладчиком стек выполнения не отображается пустым, когда эти .then (обратный вызов) микрозадачиобработано / выполнено.

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

devtools в Chrome пытается помочь в поддержании "асинхронного" стека, но это не такреальный стек.Реальный стек - это все, что находится до первой строки «async».

Являются ли обычные функции, такие как f2 (), задачей

Быть задачей или микрозадачей не значитсвойство функции.Эта же функция может быть вызвана внутри задачи, микрозадачи и других частей цикла событий, таких как рендеринг.Например:

function foo() {}

// Here, I'll call foo() as part of the current task:
foo();

// Here, I'll let the browser call foo() in a future task:
setTimeout(foo);

// Here, I'll let the browser call foo() in a microtask:
Promise.resolve().then(foo);

// Here, I'll let the browser call foo() as part of the render steps:
requestAnimationFrame(foo);

В вашем примере f2 - это , а не , вызываемый в микрозадаче.Это примерно так:

function three() {}
function two() {}

async function one() {
  await two();
  three();
}

one();

Здесь one() вызывается внутри задачи, которая выполнила сценарий.one() вызывает two() синхронно, поэтому он выполняется как часть той же задачи.Затем мы await результат вызова two().Поскольку мы await, остальная часть функции выполняется в микрозадаче.three() называется, поэтому он выполняется в одной и той же микрозадаче.

...