为什么这一行带有"await"的代码会触发微任务队列处理? [英] Why does this line of code with 'await' trigger microtask queue processing?

查看:183
本文介绍了为什么这一行带有"await"的代码会触发微任务队列处理?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

以下引号是我了解微任务队列处理的主要参考:

The following quotes are my primary references for understanding microtask queue processing:

当JS堆栈为空时,将处理微任务(承诺使用).

Microtasks (which promises use) are processed when the JS stack empties.

-杰克·阿奇博尔德

对我来说这没有意义.

事件循环的一次复现将恰好从宏任务队列中处理一个任务(此队列在WHATWG规范中简称为任务队列).此宏任务完成后,将处理所有可用的微任务,即在相同的复审周期内.

One go-around of the event loop will have exactly one task being processed from the macrotask queue (this queue is simply called the task queue in the WHATWG specification). After this macrotask has finished, all available microtasks will be processed, namely within the same go-around cycle.

-堆栈溢出

现在,关于以下代码段中的第9行(**):

Now, regarding line 9 (**) in the following snippet:

单步执行带有调试器的代码片段,当处理/执行这些.then( callback )微任务时,执行堆栈不会显示为空.

From stepping through this snippet w/ debugger, the execution stack does not appear empty when these .then( callback ) microtasks are processed/executed.

f2()这样的常规函数​​是否被视为任务(又称为宏任务)? (返回时,它是一个事件循环nextTick(),并且处理了微任务队列)

Are regular functions like f2() considered a task (aka macrotask)? (when it returns it's an event loop nextTick() and the microtask queue is processed)

当JS堆栈不为空时,为什么微任务会执行?

Why are microtasks executing when the JS stack is not empty?

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()

另一个奇特的发现-当您在第17行中的console.log(xx)之后添加xx()时,在执行微任务之前,完全清除了堆栈.

Another peculiar finding - when you add xx() right after console.log(xx) on line 17, the stack is completely cleared prior to executing the microtasks.

微任务队列处理之前的调用堆栈1步骤:

The call stack 1 step prior to microtask queue processing:

然后立即进行下一步.

Then the immediate next step.

在这两个步骤之间,处理了微任务队列.

Between these two steps, the microtask queue was processed.

在这些步骤之间,调用栈是否在[内部]清除了?

Does the call stack clear [under the hood] between these steps^?

然后根据await [expression]之后的代码所需的词汇环境创建新的调用堆栈吗?

And then is a new call stack created according to the required lexical environment(s) for code after the await [expression]?

发布此消息时,我不知道chrome调试器的调用堆栈中-----(异步)-----行下方的所有内容均是伪堆栈"的一部分.

At the time of posting this, I was not aware that everything below the -----(async)----- line in the chrome debugger's call stack was part of a 'fake stack'.

此伪堆栈"以与同步调试一致的方式用于异步调试.

This 'fake stack' is presented for async debugging in a way consistent with sync debugging.

只有-----(async)-----这一行上方的元素才是真正的主线程调用堆栈的一部分.

Only the elements above this -----(async)----- line are part of the real main thread call stack.

推荐答案

当JS堆栈为空时,将处理微任务(承诺使用)." -杰克·阿奇博尔德(对我没有意义)

"Microtasks (which promises use) are processed when the JS stack empties." -Jake Archibald (doesn't make sense to me)

调用堆栈"是当前正在执行的事情的列表:

The "call stack" is the list of things that are currently executing:

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

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

bar();

当我们点击第一个调试器语句时,脚本仍在执行,barfoo也是如此.由于存在父子关系,因此堆栈为script > bar > foo.当我们点击第二个调试器语句时,foo已完成执行,因此不再存在于堆栈中.堆栈是script > bar.

When we hit the first debugger statement, the script is still executing, as is bar, as is foo. Since there's a parent-child relationship, the stack is script > bar > foo. When we hit the second debugger statement, foo has finished executing, so it's no longer on the stack. The stack is script > bar.

当堆栈变空时,微任务队列将一直处理到空为止.

The microtask queue is processed until it's empty, when the stack becomes empty.

事件循环的一次复现将恰好从宏任务队列中处理一个任务(此队列在WHATWG规范中简称为任务队列).此宏任务完成后,将处理所有可用的微任务,即在相同的复飞周期内." -stackoverflow

"One go-around of the event loop will have exactly one task being processed from the macrotask queue (this queue is simply called the task queue in the WHATWG specification). After this macrotask has finished, all available microtasks will be processed, namely within the same go-around cycle." - stackoverflow

编辑:我一直将上面的宏任务"读为微任务".浏览器中并没有真正的宏任务队列之类的东西,它只是一个任务队列.

Edit: I kept reading "macrotask" above as "microtask". There isn't really such a thing as a macrotask queue in the browser, it's just a task queue.

虽然在处理任务之后确实有一个微任务处理点,​​但实际上只有在这里处理排队任务以使微任务排队的规范,而无需先调用JS.在大多数情况下,当JS堆栈清空时,微任务队列就会清空.

Although it's true that there's a microtask processing point after processing a task, it's only really there to handle specifications that queue tasks to queue microtasks, without calling into JS first. Most of the time, the microtask queue is emptied when the JS stack empties.

从带调试器的代码段开始,当处理/执行这些.then(callback)微任务时,执行堆栈不会显示为空.

From stepping through this snippet w/ debugger, the execution stack does not appear empty when these .then( callback ) microtasks are processed/executed.

在执行回调时,堆栈永远不会为空,因为回调本身将位于堆栈上.但是,如果这是堆栈上唯一的东西,则可以假定在调用此回调之前堆栈是空的.

The stack will never be empty while callbacks are being executed, since the callback itself will be on the stack. However, if this is the only thing on the stack, you can assume the stack was empty before this callback was called.

Chrome的devtools试图帮助维护异步"堆栈,但这不是真正的堆栈.真正的堆栈是第一行异步"行之前的所有内容.

Chrome's devtools tries to be helping in maintaining an "async" stack, but this isn't the real stack. The real stack is everything before the first "async" line.

是否有像f2()这样的常规函数​​被视为任务

Are regular functions like f2() considered a task

成为任务或微任务不是函数的属性.可以在任务,微任务和事件循环的其他部分(例如渲染)中调用相同的函数.例如:

Being a task or a microtask isn't a property of a function. The same function can be called within a task, a microtask, and other parts of the event loop such as rendering. Eg:

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.有点像这样:

In your example, f2 is not called within a microtask. It's kinda like this:

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

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

one();

在这里,在执行脚本的任务中调用one(). one()同步调用two(),因此它作为同一任务的一部分运行.然后,我们await调用two()的结果.因为我们await,所以该函数的其余部分在微任务中运行. three()被调用,因此它在相同的微任务中运行.

Here, one() is called within the task that executed the script. one() calls two() synchronously, so it runs as part of the same task. We then await the result of calling two(). Because we await, the rest of the function runs in a microtask. three() is called, so it runs in the same microtask.

这篇关于为什么这一行带有"await"的代码会触发微任务队列处理?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆