我是JS的新手,正在try 弄清楚当代码具有微任务时的执行顺序.我知道它背后的理论,即当堆栈为"空"时(即当它上只有全局上下文时),微任务会从队列推送到调用堆栈. 例如,在全球环境中,我们有两个连续的循环:

for(let i = 0; i<1000; i++){
    console.log(i);
};

for(let i = 0; i<1000; i++){
    console.log(i);
};

在执行for循环之前、之后和期间,是否满足了堆栈为空的条件?如果是,如果到那时它已在队列中并且调用栈为空,为什么下面代码中的微任务(res)=> { console.log(res)}在最后两个循环之间没有执行?

const promise = new Promise(function(resolve, _){
    resolve('microtask2');
});

for(let i = 0; i<1000; i++){
    console.log(i);
};

promise.then((res)=> {
    console.log(res);
)};

for(let i = 0; i<1000; i++){
    console.log(i);
};

for(let i = 0; i<1000; i++){
    console.log(i);
};

以下是我对执行顺序的看法:

  1. promise的构造函数中的执行者函数调用resolve()并立即结算promise.
  2. 执行循环.
  3. 调用then(),将其上下文放入调用堆栈(在全局上下文之上)
  4. then()内的回调在浏览器环境中的某个地方注册.
  5. then上下文从堆栈中弹出.
  6. 执行另一个循环,因为来自then()的回调仅注册并且尚未进入队列(或者它是否立即进入队列?)
  7. 既然循环已经完成并且调用堆栈为is empty,事件循环从队列中拾取回调,将其扔到调用堆栈并执行(我知道情况并非如此,回调将最后打印,但我不知道为什么)
  8. 执行最后一个循环

通过步骤7,执行回调的所有条件都得到满足: a)堆栈上没有任何内容 b)没有同步代码正在运行 c)回调正在队列中等待

如果循环之间有很多机会可以执行,为什么回调会留在队列中,直到执行最后一个循环?


Edited to address Scott Marcus' commentary

整个问题的灵感来自以下代码的意外行为(嗯,至少对我来说是意外的):

const timer = setTimeout(function(){
    console.log('set timeout');
}, 0);

new Promise(function(resolve, _){
    try{   
        resolve('microtask1');
    }
    catch(err){
        console.error(`caught an ${err}`);
    }
}).then((res)=>{
    console.log(res);
});
console.log('synchronous code 1');

const result = await fetch(`https://jsonplaceholder.typicode.com/photos`);

console.log('synchronous code 2');

一开始,我认为只有在文件中的最后一行同步代码被执行(在本例中是console.log('synchronous code 2'))后才会执行micro-/macrotasks,而这正是我所看到的,直到我在Inbox函数外执行了await声明. 如果运行上面的代码,您将得到以下输出:

enter image description here

如果我将await声明包装在一个像这样的Deliverc函数中:

let result;
const timer = setTimeout(function(){
    console.log('set timeout');
}, 0);

new Promise(function(resolve, _){
    try{   
        resolve('microtask1');
    }
    catch(err){
        console.error(`caught an ${err}`);
    }
}).then((res)=>{
    console.log(res);
});
console.log('synchronous code 1');

(async function(){
    result = await fetch(`https://jsonplaceholder.typicode.com/photos`);
})();

console.log('synchronous code 2');

...输出将会有所不同(正是我所期望的):

enter image description here

11个小时以来,我一直在试图弄清楚为什么顶级await人表现出这种行为,但我仍然远未找到答案.如果您有机会对此有所了解,我将不胜感激.

推荐答案

在执行for循环之前、之后和期间,是否满足了堆栈为空的条件?

否.全局执行上下文仍然在堆栈上.整个代码运行至完成,只有在此之后堆栈变空并且同步任务才能运行.

这正是我所看到的,直到我在spuc函数外部执行await声明.

await之前,模块判断执行上下文都在堆栈上.一旦执行达到await,模块代码的执行就会暂停,并从堆栈中弹出上下文.对于TLA,它下面没有任何内容,并且堆栈变成空的;在async函数中,它只会从调用中返回(promise).

一旦后来解决了promise,上下文的执行就会在其自己的同步微任务中恢复.

Javascript相关问答推荐

无法通过从我的 node 后台创建的预签名URL将PDF上传到S3桶

我的JS代码将按照哪个序列被解释

如何指定1条记录1个表?

添加/删除时React图像上传重新渲染上传的图像

Flisk和JS错误:未捕获的Syntax错误:意外的令牌'<'

如何在JavaScript中在文本内容中添加新行

单击更新页面的按钮后,页面刷新;测试/断言超时,有两个标题,但没有一个标题

yarn安装一个本地npm包,以便本地包使用main项目的node_modules(ckeditor-duplicated-modules错误)

如何用显示网格平滑地将元素从一个地方移动到另一个地方?

我不知道为什么setwritten包装promise 不能像我预期的那样工作

Rehype将hashtag呈现为URL

在使用HighChats时如何避免Datatables重新初始化错误?

使用Promise.All并发解决时,每个promise 的线性时间增加?

Html文件和客户端存储的相关问题,有没有可能?

无法使用单击按钮时的useState将数据从一个页面传递到另一个页面

如何从HTML对话框中检索单选项组的值?

当我try 将值更改为True时,按钮不会锁定

与svg相反;S getPointAtLength(D)-我想要getLengthAtPoint(x,y)

用于测试其方法和构造函数的导出/导入类

与在编剧中具有动态价值的定位器交互