我是 JS 新手,正在尝试弄清楚代码具有微任务时的执行顺序。我知道其背后的理论,当堆栈为“空”时(即,当其上只有全局上下文时),微任务被从队列推送到调用堆栈。 例如,在全局上下文中,我们有两个连续的循环:
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);
};
这是我查看执行顺序的方式:
resolve()
并立即结算 Promise。then()
被唤起,它的上下文放在调用堆栈上(在全局上下文之上)then()
内的回调已在浏览器环境中的某个位置注册。then
上下文从堆栈中弹出。then()
的回调仅被注册,尚未进入队列(或者它立即进入队列?)到第7步,回调执行的所有条件都满足了: a) 堆栈上没有任何内容 b) 没有同步代码正在运行 c) 回调正在队列中等待
如果循环之间有很多机会执行,为什么回调会坐在队列中直到执行最终循环?
编辑以解决斯科特·马库斯的评论
整个问题的灵感来自以下代码的意外行为(嗯,至少对我来说是意外的):
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')
),这正是我在执行await
之前所看到的情况
异步函数外部的语句。
如果运行上面的代码,您将得到以下输出:
如果我将
await
语句包装在异步函数中,如下所示:
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');
...输出会有所不同(正是我所期望的):
我已经尝试找出为什么顶级
await
会表现出这种行为 11 个小时了,但我仍然没有找到答案。如果您有机会对此有所了解,我将不胜感激。
for循环执行前、执行后和执行过程中是否都满足堆栈为空的条件?
不。全局执行上下文仍在堆栈上。整个代码运行完成,之后堆栈变空,异步任务才能运行。