跳到主要内容

事件循环

2025年03月19日
柏拉文
越努力,越幸运

一、Promise执行顺序


async function async1() {
console.log("1");
await 1;
console.log("2");
}

async1();

console.log("3");

Promise.resolve()
.then(() => {
console.log("4");
})
.then(() => {
console.log("5");
})
.then(() => {
console.log("6");
});


// 结果: 1 3 2 4 5 6

二、Thenable对象执行


async function async1() {
console.log("1");
await {
then(cb) {
cb();
},
};
console.log("2");
}

async1();

console.log("3");

Promise.resolve()
.then(() => {
console.log("4");
})
.then(() => {
console.log("5");
})
.then(() => {
console.log("6");
});

// 结果: 1 3 4 2 5 6

三、Promise对象执行


async function async1() {
console.log("1");
await new Promise((resolve,reject)=>{
resolve();
})
console.log("2");
}

async1();

console.log("3");

Promise.resolve()
.then(() => {
console.log("4");
})
.then(() => {
console.log("5");
})
.then(() => {
console.log("6");
});

// 结果: 1 3 4 2 5 6

四、异步函数执行顺序


async function async1() {
console.log("1");
await async2();
console.log("2");
}

async function async2() {
console.log("3");
}

console.log("5");

setTimeout(() => {
console.log("6");
}, 0);

async1();

new Promise((resolve) => {
console.log("7");
resolve();
})
.then(() => {
console.log("8");
})
.then(() => {
console.log("9");
})
.then(() => {
console.log("10");
});

console.log("11");

// 结果: 5 1 3 7 11 2 8 9 10 6
  1. 事件循环从宏任务 macrosTack 队列开始。此时在宏任务队列中, 只有一个script(整体代码)任务。从宏任务队列中取出一个任务来执行先执行同步代码

  2. 首先执行 console.log("5");, 输出 5

  3. 执行setTimeout, 将setTimeout的回调函数放入到macroTask队列

  4. 接着执行async1函数,输出1

  5. 遇到await async2();, 同步执行async2()

  6. 执行async2() 函数中的 console.log("3");, 输出3

  7. 由于async2() 函数返回的是字符串, 立即更新成功状态。那么 await async2(); 之后的代码 console.log("2"); 会整体作为一个微任务, 立即加入到微任务microTask队列

  8. 遇到new Promise(), 同步执行console.log("7"); , 输出7

  9. .then()依次按序加入到微任务microTask队列

  10. 执行同步代码console.log("11");, 输出11

  11. script 宏任务执行完毕,开始检查微任务microTask队列, 依次执行, 清空微任务队列

  12. 执行微任务一console.log("2");, 输出2; 执行微任务二console.log("8");, 输出8; 执行微任务三console.log("9");, 输出9; 执行微任务四console.log("10");, 输出10

  13. 按序从宏任务队列macroTask中去一个任务放入主栈执行, 执行console.log("6");, 输出6

五、异步返回字符串


async function async1() {
console.log("1");
await async2();
console.log("2");
}

async function async2() {
console.log("3");
return "async2 函数";
}

console.log("5");

setTimeout(() => {
console.log("6");
}, 0);

async1();

new Promise((resolve) => {
console.log("7");
resolve();
})
.then(() => {
console.log("8");
})
.then(() => {
console.log("9");
})
.then(() => {
console.log("10");
});

console.log("11");

// 结果: 5 1 3 7 11 2 8 9 10 6
  1. 事件循环从宏任务 macrosTack 队列开始。此时在宏任务队列中, 只有一个script(整体代码)任务。从宏任务队列中取出一个任务来执行先执行同步代码

  2. 首先执行 console.log("5");, 输出 5

  3. 执行setTimeout, 将setTimeout的回调函数放入到macroTask队列

  4. 接着执行async1函数,输出1

  5. 遇到await async2();, 同步执行async2()

  6. 执行async2() 函数中的 console.log("3");, 输出3

  7. 由于async2() 函数返回的是字符串, 立即更新成功状态。那么 await async2(); 之后的代码 console.log("2"); 会整体作为一个微任务, 立即加入到微任务microTask队列

  8. 遇到new Promise(), 同步执行console.log("7"); , 输出7

  9. .then()依次按序加入到微任务microTask队列

  10. 执行同步代码console.log("11");, 输出11

  11. script 宏任务执行完毕,开始检查微任务microTask队列, 依次执行, 清空微任务队列

  12. 执行微任务一console.log("2");, 输出2; 执行微任务二console.log("8");, 输出8; 执行微任务三console.log("9");, 输出9; 执行微任务四console.log("10");, 输出10

  13. 按序从宏任务队列macroTask中去一个任务放入主栈执行, 执行console.log("6");, 输出6

六、异步返回Thenable


async function async1() {
console.log("1");
await async2();
console.log("2");
}

async function async2() {
console.log("3");
return {
then(cb) {
cb();
},
};
}

console.log("5");

setTimeout(() => {
console.log("6");
}, 0);

async1();

new Promise((resolve) => {
console.log("7");
resolve();
})
.then(() => {
console.log("8");
})
.then(() => {
console.log("9");
})
.then(() => {
console.log("10");
});

console.log("11");

// 结果: 5 1 3 7 11 8 2 9 10 6
  1. 事件循环从宏任务 macrosTack 队列开始。此时在宏任务队列中, 只有一个script(整体代码)任务。从宏任务队列中取出一个任务来执行先执行同步代码

  2. 首先执行 console.log("5");, 输出 5

  3. 执行setTimeout, 将setTimeout的回调函数放入到macroTask队列

  4. 接着执行async1函数,输出1

  5. 遇到await async2();, 同步执行async2()

  6. 执行async2() 函数中的 console.log("3");, 输出3

  7. 由于async2() 函数返回的是thenable, 延迟1then()后更新成功状态。那么 await async2(); 之后的代码 console.log("2"); 会整体作为一个微任务, 延迟1then()后加入到微任务microTask队列

  8. 遇到new Promise(), 同步执行console.log("7"); , 输出7

  9. .then()依次按序加入到微任务microTask队列。此时微任务队列为[console.log("8");,console.log("2");,console.log("9");,console.log("10");]

  10. 执行同步代码console.log("11");, 输出11

  11. script 宏任务执行完毕,开始检查微任务microTask队列, 依次执行, 清空微任务队列

  12. 执行微任务一console.log("8");, 输出8; 执行微任务二console.log("2");, 输出2; 执行微任务三console.log("9");, 输出9; 执行微任务四console.log("10");, 输出10

  13. 按序从宏任务队列macroTask中去一个任务放入主栈执行, 执行console.log("6");, 输出6

七、异步返回Promise


async function async1() {
console.log("1");
await async2();
console.log("2");
}

async function async2() {
console.log("3");
return new Promise((resolve, reject) => {
resolve();
console.log("4");
});
}

console.log("5");

setTimeout(() => {
console.log("6");
}, 0);

async1();

new Promise((resolve) => {
console.log("7");
resolve();
})
.then(() => {
console.log("8");
})
.then(() => {
console.log("9");
})
.then(() => {
console.log("10");
});

console.log("11");


// 结果: 5 1 3 4 7 11 8 9 2 10 6
  1. 事件循环从宏任务 macrosTack 队列开始。此时在宏任务队列中, 只有一个script(整体代码)任务。从宏任务队列中取出一个任务来执行先执行同步代码

  2. 首先执行 console.log("5");, 输出 5

  3. 执行setTimeout, 将setTimeout的回调函数放入到macroTask队列

  4. 接着执行async1函数,输出1

  5. 遇到await async2();, 同步执行async2()

  6. 执行async2() 函数中的 console.log("3");, 输出3

  7. 由于async2() 函数返回的是Promise, 延迟2then()后更新成功状态。那么 await async2(); 之后的代码 console.log("2"); 会整体作为一个微任务, 延迟2then()后加入到微任务microTask队列

  8. 遇到new Promise(), 同步执行console.log("7"); , 输出7

  9. .then()依次按序加入到微任务microTask队列。此时微任务队列为[console.log("8");,console.log("9");,console.log("2");,console.log("10");]

  10. 执行同步代码console.log("11");, 输出11

  11. script 宏任务执行完毕,开始检查微任务microTask队列, 依次执行, 清空微任务队列

  12. 执行微任务一console.log("8");, 输出8; 执行微任务二console.log("9");, 输出9; 执行微任务三console.log("2");, 输出2; 执行微任务四console.log("10");, 输出10

  13. 按序从宏任务队列macroTask中去一个任务放入主栈执行, 执行console.log("6");, 输出6

八、事件循环基础


问题

async function async1() {
console.log('async1 start');
await async2();
console.log('async1 end');
}
async function async2() {
console.log('async2');
}

console.log('script start');

setTimeout(function() {
console.log('setTimeout');
}, 0)

async1();

new Promise(function(resolve) {
console.log('promise1');
resolve();
}).then(function() {
console.log('promise2');
});
console.log('script end');

结果

script start
async1 start
async2
promise1
script end
async1 end
promise2
setTimeout

解释:

  1. 事件循环从宏任务macrostack队列开始,这个时候,宏任务队列中,只有一个script(整体代码)任务。从宏任务队列中取出一个任务来执行

  2. 首先执行 console.log('script start'),输出 'script start'

  3. 遇到 setTimeoutconsole.log('setTimeout') 放到 macrotask 队列中

  4. 执行 aync1() 输出 'async1 start''async2' ,把 console.log('async1 end') 放到 micro 队列中

  5. 执行到 promise ,输出 'promise1' ,把 console.log('promise2') 放到 micro 队列中

  6. 执行 console.log('script end'),输出 'script end'

  7. macrotask 执行完成会执行 microtask ,把 microtask quene 里面的 microtask 全部拿出来一次性执行完,所以会输出 'async1 end''promise2'

  8. 开始新一轮的事件循环,去除执行一个 macrotask 执行,所以会输出 'setTimeout'

总结:

  1. await 表达式 语句是同步的

  2. await 表达式; …… 之后的表达式会进行封装统一加入到微任务队列

九、事件循环进阶


问题

async function async1() {
console.log('async1 start');
await async2();
console.log('async1 end1');
await async3();
console.log('async1 end2');
}
async function async2() {
console.log('async2');
}
async function async3(){
console.log("async3")
}

console.log('script start');

setTimeout(function() {
console.log('setTimeout');
}, 0)

async1();

new Promise(function(resolve) {
console.log('promise1');
resolve();
}).then(function() {
console.log('promise2');
});
console.log('script end');

结果

script start
async1 start
async2
promise1
script end
async1 end1
async3
promise2
async1 end2
setTimeout