例子一

setTimeout(function() {
    console.log('setTimeout');
})
new Promise(function(resolve) {
    console.log('promise');
}).then(function() {
    console.log('then');
})
console.log('console');
  • 整段代码做为宏任务,进入主线程
  • 遇到setTimeout,将其回调函数注册后分发到宏任务事件队列中(作为下一次循环宏任务调用)
  • 接下来遇到了Promisenew Promise立即执行,打印'promise',then函数分发到微任务事件队列(本次宏任务的微任务)
  • 遇到console.log(),立即执行
  • 整体代码作为第一次循环的宏任务执行结束,查看还有微任务then,执行
  • 开始第二次循环,执行setTimeout,打印'promise'
  • 结果:promise, console, then, setTimeout

例子二

console.log('Hello World!');
const p1 = new Promise((resolve) => {
  console.log(3);
  resolve(4);
});
const p2 = new Promise((resolve) => {
  console.log(1);
  setTimeout(() => {
    console.log(6)
  }, 0);
  resolve(2);
});
p1.then((res) => {
  console.log(res)
});
p2.then((res) => {
  console.log(res)
});
console.log(5);
  • 整体代码作为宏任务进入主线程,先执行打印'hello world'
  • 然后p1中打印'3',resolve(4)then函数中分发到微任务事件队列
  • 然后进入p2中打印'1',遇到setTimeout,将其回调函数注册分发到宏任务事件队列
  • resolve(2)then函数中分发到微任务事件队列;向下执行,再打印'5'
  • 第一次循环的宏任务执行完,查看微任务事件队列,先打印'4',再打印'2'
  • 第二次循环,执行宏任务中的setTimeout,打印'6'
  • 结果:hello world,3,1,5,4,2,6

例子三

console.log('1');
setTimeout(function first () {
    console.log('2');
    setTimeout(function third () {
      new Promise(function(resolve) {
        console.log('3');
        resolve();
    }).then(function() {
        console.log('4')
      })
    })
})
new Promise(function(resolve) {
    console.log('5');
    resolve();
}).then(function() {
    console.log('6')
})
setTimeout(function second () {
    console.log('7');
    new Promise(function(resolve) {
        console.log('8');
        resolve();
    }).then(function() {
        console.log('9')
    })
})
  • 整体代码作为第一次宏任务,进入主线程执行,先打印'1',再将setTimeout的回调函数first注册分发到宏任务事件队列
  • 向下执行,进入Promise中打印'5',then函数分发到微任务事件队列
  • 再遇到一个setTimeout,将其回调函数second注册分发到宏任务事件队列中
  • 第一次循环的宏任务完成,执行微任务的then函数,打印'6'
  • 开始第二次循环,执行宏任务队列中的first函数打印'2';然后又遇到了setTimeout函数,将third函数注册分发到宏任务事件队列中(下次循环才会调用)
  • 接着,执行本次宏任务队列中的second函数打印'7',进入promise打印'8',then函数注册分发到微任务事件队列
  • 第二次循环的宏任务完成,执行微任务的then函数,打印'9'
  • 开始第三次循环,执行宏任务队列中third函数,先打印'3',再执行then中打印'4'
  • 结果:1,5,6,2,7,8,9,3,4

总结

  • 所有的同步任务都在主线程上执行,形成一个执行栈
  • 主线程之外,还存在一个任务队列,只要异步任务有了运行结果,就在任务队列中放置一个事件
  • 一旦执行栈中所有同步任务执行完毕,系统就会读取任务队列中的事件,异步任务结束等待状态,进入执行栈,开始执行
  • 重复上一步(事件轮循——重复读取主线程和任务队列中的事件)
  • 在js中的任务做更精确的定义:

    • macro-task(宏任务): 整体代码js,setTimeout,setInterval
    • micro-task(微任务): Promise.then,process.nextTick(node.js)
  • 在本次循环执行中,先执行宏任务,再执行微任务,都完成后,开始第二轮循环,执行宏任务,执行微任务,直到没有宏任务,完成执行
  • 图片来源


Linson
15 声望9 粉丝

加班写bugs