Skip to content

实战考察事件循环

基础执行顺序

js
// 原则:程序从上到下执行的过程中,遇到同步代码就执行,遇到异步代码就先放到对应的任务队列中(微任务放入微任务队列,宏任务放入宏任务队列),等到任务队列清空了再向下执行
// 事件循环的顺序:同步代码 → 微任务队列清空 → 宏任务队列
// 1. 先执行同步代码,输出 start end,然后将宏任务 setTimeout 推入到宏任务队列中,将微任务.then推入到微任务队列中
// 2. 执行微任务队列,输出 promise1 promise2
// 3. 执行宏任务队列,输出 setTimeout
// 输出:start end promise1 promise2 setTimeout
console.log('start');

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

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

console.log('end');

嵌套的宏任务和微任务

js
// 嵌套特点:如果在宏任务执行过程中又产生了微任务,会立即执行这些微任务,然后再继续执行下一个宏任务
// 注意:队列的特性是先入先出,所以越先入队列的任务越先执行
// 原则:程序从上到下执行的过程中,遇到同步代码就执行,遇到异步代码就先放到对应的任务队列中(微任务放入微任务队列,宏任务放入宏任务队列),等到任务队列清空了再向下执行(以此类推,遇到就推入)
// 事件循环的顺序:同步代码 → 微任务队列清空 → 宏任务队列(如果宏任务中有微任务,那么当前宏任务执行完就执行当前宏任务的微任务,等所有的宏任务的微任务队列执行完再执行宏任务队列中的下一个宏任务)
// 1. 先执行同步代码,输出 start end,然后将将宏任务 setTimeout 推入到宏任务队列中,将微任务.then推入到微任务队列中
// 2. 执行微任务,输出 promise1,将 promise 内的 setTimeout 推入到宏任务队列中,然后开始执行宏任务队列
// 3. 执行宏任务队列第一个宏任务,输出 timeout1,然后将当前宏任务的微任务推入到微任务队列中
// 4. 执行宏任务的微任务,输出 promise-in-timeout
// 5. 执行下一个宏任务,输出 timeout-in-promise
// 输出:start end promise1 timeout1 promise-in-timeout timeout-in-promise
console.log('start');

setTimeout(function() {
  console.log('timeout1');
  Promise.resolve().then(function() {
    console.log('promise-in-timeout');
  });
}, 0);

Promise.resolve().then(function() {
  console.log('promise1');
  setTimeout(function() {
    console.log('timeout-in-promise');
  }, 0);
});

console.log('end');

嵌套在微任务中的微任务

js
// 1、总结:微任务执行完,如果内部有微任务则继续推送到微任务队列中继续执行微任务直至清空微任务队列,再执行下一个宏任务
// 2、过程:先执行同步代码 start end --- 将微任务推入队列 --- 将宏任务推入队列 --- 执行微任务队列 promise1 ---
// 将微任务的微任务推入微任务队列 --- 执行微任务队列 promise2 --- 执行宏任务:timeout
// 3、结果:start end promise1 promise2 timeout
console.log('start');

Promise.resolve().then(() => {
console.log('promise1');
Promise.resolve().then(() => {
  console.log('promise2');
});
});

setTimeout(() => {
console.log('timeout');
}, 0);

console.log('end');

嵌套的组合任务

js
// 1、总结:new Promise()传入的回调函数内执行的程序属于同步代码,只有.then是微任务,遇到微任务就将其推入到微任务队列中然后执行这个微任务队列
// 2、过程如下:先执行同步代码,1 5 10 --- 在执行微任务队列 6 --- 开始执行宏任务队列 2 3,再执行其微任务 4 --- 执行下一个宏任务 7 8,然后执行其微任务 9
// 3、结果:1 5 10 6 2 3 4 7 8 9
console.log('1');

setTimeout(function() {
console.log('2');
new Promise(function(resolve) {
  console.log('3');
  resolve();
}).then(function() {
  console.log('4');
});
}, 0);

new Promise(function(resolve) {
console.log('5');
resolve();
}).then(function() {
console.log('6');
});

setTimeout(function() {
console.log('7');
new Promise(function(resolve) {
  console.log('8');
  resolve();
}).then(function() {
  console.log('9');
});
}, 0);

console.log('10');

// 输出 1 5 10 6 2 3 4 7 8 9

async 和 await 任务

js
// 1、总结:函数调用属于同步代码 --- await 右侧的函数也属于同步代码 --- 只有 await 以下的程序属于微任务
// 2、过程如下:先执行同步代码,4 1 3 6 8 --- 在执行微任务队列 2 7 --- 然后执行宏任务队列 5
// 3、输出:4 1 3 6 8 2 7 5
async function async1() {
console.log('1');
await async2();
console.log('2');
}

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

console.log('4');

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

async1();

new Promise(function(resolve) {
console.log('6');
resolve();
}).then(function() {
console.log('7');
});

console.log('8');

其他

js
// 陷阱 1:new Promise() 中的回调函数属于同步代码
// 陷阱 2:.then 执行的前提是此promise 对象必须内部执行了 resolve() 或 reject()
const promise = new Promise((resolve, reject) => {
console.log(1)
console.log(2)
})
promise.then(() => {
console.log(3)
})
console.log(4)
// 输出:1 2 4