为什么这行带有“await”的代码会触发微任务队列处理?

2024-05-18

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

当 JS 堆栈清空时,就会处理微任务(承诺使用)。

- 杰克·阿奇博尔德 https://twitter.com/jaffathecake/status/954653170986311680

这对我来说没有意义。

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

- 堆栈溢出 https://stackoverflow.com/a/25933985/1541563

现在,关于以下代码片段中的第 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步:

然后立即进行下一步。

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

这些步骤之间的调用堆栈是否清除[在引擎盖下]^?

然后是根据代码所需的词法环境创建一个新的调用堆栈await [expression]?

编辑:在发布本文时,我不知道 chrome 调试器调用堆栈中 -----(async)----- 行下方的所有内容都是“假堆栈”的一部分。

这个“假堆栈”以与同步调试一致的方式用于异步调试。

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


“当 JS 堆栈清空时,就会处理微任务(承诺使用)。” -杰克阿奇博尔德(对我来说没有意义)

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

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

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

bar();

当我们点击第一个调试器语句时,脚本仍在执行,如下所示bar, as is foo。由于存在父子关系,所以栈是script > bar > foo。当我们点击第二个调试器语句时,foo已经执行完毕,所以它不再在堆栈上。堆栈是script > bar.

微任务队列被处理直到它为空,此时堆栈变空。

“事件循环的一次循环将恰好从宏任务队列(该队列在 WHATWG 规范中简称为任务队列)中处理一个任务。在该宏任务完成后,将处理所有可用的微任务,即相同的复飞周期。” - 堆栈溢出

Edit:我一直将上面的“宏任务”读作“微任务”。浏览器中实际上并不存在宏任务队列之类的东西,它只是一个任务队列。

虽然处理完任务后确实有一个微任务处理点,​​但它只是真正处理队列任务到队列微任务的规范,而不是先调用JS。大多数时候,当JS堆栈清空时,微任务队列也随之清空。

通过使用调试器逐步执行此代码片段,当处理/执行这些 .then(callback) 微任务时,执行堆栈不会显示为空。

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

Chrome 的开发工具试图帮助维护“异步”堆栈,但这不是真正的堆栈。真正的堆栈是第一个“异步”行之前的所有内容。

像 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 is not在微任务中调用。有点像这样:

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

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

one();

Here, one()在执行脚本的任务中调用。one() calls two()同步,因此它作为同一任务的一部分运行。然后我们await调用的结果two()。因为我们await,函数的其余部分在微任务中运行。three()被调用,因此它在同一个微任务中运行。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么这行带有“await”的代码会触发微任务队列处理? 的相关文章

随机推荐