宏任务与微任务

原文:做一些动图,学习一下EventLoop (https://juejin.cn/post/6969028296893792286)

一、任务队列

  JavaScript 是单线程执行的语言, 在同一时间只能干一件事情。如果前面的任务很耗时后面的任务就会一直等待,为了解决这个问题,js中出现了同步任务和异步任务

1.1 同步任务

  在主线程上排队执行的任务只有前面执行完毕才能执行下一个任务,形成一个执行栈

1.2 异步任务

  不进入主线程,而是进入任务队列,只有当主线程的任务执行完毕,才会从任务队列中拿任务到主线程来执行。由于主线程不断循环 获取任务 -> 执行任务 -> 再次获取任务 -> 再次执行任务 ,所以这种机制被叫做 事件循环

异步任务执行顺序图解

二、宏任务和微任务

  在任务队列中其实还分为宏任务队列和微任务队列,里面存放的就是宏任务和微任务

  宏任务和微任务都是异步任务, 这两者的区别就是它们的执行顺序。

  在同步任务中, 任务都是讲究按照代码顺序执行的, 而异步任务也是需要按照顺序执行的; 队列的属性就说先进先出, 因此异步任务会按照进入队列的顺序以此执行。

  这就解释了为什么下列代码中,为什么后面的定时器比前面的定时器先执行。因为后者的定时器先推进宏任务队列,而前者会在到时间后再被推进宏任务队列

setTimeout(() => {
    console.log('a');
}, 10000);

setTimeout(() => {
    console.log('b');
}, 100);

2.1 宏任务

  宏任务包括:script(整体代码), setTimeout, setInterval, Ajax, DOM事件, requestAnimationFrame ( 只存在浏览器中 ), I/O, SetImmediate( node )

2.2 微任务

  微任务包括:Promise.then, async/await, Object.observe ( 已废弃 ), MutationObserver ( HTML5新特性, 只存在浏览器中 ), process.nextTick ( node )

三、完整的执行顺序

  1. 从上往下执行所有的同步代码
  2. 在执行过程中遇到宏任务就存放到宏任务队列中, 遇到微任务就存放到微任务队列中
  3. 当所有的同步任务执行完毕, 就执行微任务队列中满足需求的所有回调( process.nextTick 优先 )
  4. 当微任务队列中所有满足需求的回调执行完毕后, 就执行宏任务队列中满足需求的所有回调 ( SetImmediate 最后 )

3.1 注意

  1. 每执行完一个宏任务都会立即检查微任务列表有没有清空, 如果没有就立即清空。
  2. Promise里的是同步任务,立即执行
  3. Promise.then() 中如果不写函数的话也是同步任务

四、练习题

4.1 第一题

console.log(100)
setTimeout(() => {
    console.log(200)
})
Promise.resolve().then(() => {
    console.log(300)
})
console.log(400)

// 100、400、300、200
// 1. 第一遍执行同步任务输出 100、400
// 2. 遇到 setTimeout 将其加入宏任务队列、Promise.then 将其加入微任务队列
// 3. 先执行微任务输出 300
// 4. 再执行宏任务输出 200

4.2 第二题

setTimeout(function() {
    console.log('a')
});

new Promise(function(resolve) {
    console.log('b');

    for (var i = 0; i < 10000; i++) {
        i == 99 && resolve();
    }
}).then(function() {
    console.log('c')
});

console.log('d');

// b、d、c、a
// 1. 先执行同步任务, 输出b、d ( 注意:Promise里的是同步任务 )
// 2. 遇到 setTimeout 将其加入宏任务队列、Promise.then 将其加入微任务队列
// 3. 先执行微任务输出 c
// 4. 再执行宏任务输出 a

4.3 第三题

let promise = new Promise((resolve) => {
  setTimeout(() => {
    console.log('a')
    resolve()
  }, 0)
  console.log('b')
}).then(value => console.log('c'))

console.log('d')

// b、d、a、c

// 解析:
// 读取到第 2 行, 将 setTimeout 加入宏任务队列
// 读取到第 6 行,执行输出 b
// 读取到第 7 行,因为还没有执行 resolve() 所以暂时不会创建 Promise.then
// 读取到第 9 行,执行输出 d
// 由于没有微任务,开始执行宏任务
// 读取 setTimeout 的代码, 输出 a, 并且创建 Promise.then
// 执行微任务,输出 c

4.4 第四题

console.log('a');

setTimeout(function() {
    console.log('b');
    process.nextTick(function() {
        console.log('c');
    })
    new Promise(function(resolve) {
        console.log('d');
        resolve();
    }).then(function() {
        console.log('e')
    })
})
process.nextTick(function() {
    console.log('f');
})
new Promise(function(resolve) {
    console.log('g');
    resolve();
}).then(function() {
    console.log('h')
})

setTimeout(function() {
    console.log('i');
    process.nextTick(function() {
        console.log('j');
    })
    new Promise(function(resolve) {
        console.log('k');
        resolve();
    }).then(function() {
        console.log('l')
    })
})


// 第一轮
// 1.1. 进入主线程,遇到 console.log('a'), 执行打印 a
// 1.2. 遇到 setTimeout 将其加入到 宏任务队列
// 1.3. 遇到 process.nextTick 将其加入到微任务队列
// 1.4. 遇到 Promise 将同步任务 g 打印
// 1.5. 将 Promise.then 加入到微任务队列
// 1.6. 遇到第二个 setTimeout 将其加入到 宏任务队列

// 打印情况: a、g
// 宏任务( 由底到高 ):setTimeout (P3)、setTimeout (P26)
// 微任务( 由底到高 ):process.nextTick (P16)、Promise.then (P22)

// 1.7 查找微任务队列,由下开始执行
// 1.8 执行 process.nextTick, 打印 f
// 1.9 执行 Promise.then, 打印 h
// 打印情况: a、g、f、h



// 第二轮
// 2.1 从宏任务队列底部开始执行 setTimeout (P3)
// 2.2 遇到 console.log('b') 打印 b
// 2.3 遇到 process.nextTick 加入到微任务队列
// 2.4 遇到 Promise 将同步任务 d 打印
// 2.5 将 Promise.then 加入到微任务队列

// 打印情况: a、g、f、h、b、d
// 宏任务( 由底到高 ):setTimeout (P26)
// 微任务( 由底到高 ):process.nextTick (P5)、Promise.then (P11)

// 2.6 查找微任务队列,由下开始执行
// 2.7 执行 process.nextTick, 打印 c
// 2.8 执行 Promise.then, 打印 e
// 打印情况: a、g、f、h、b、d、c、e



// 第三轮
// 3.1 从宏任务队列底部开始执行 setTimeout (P26)
// 3.2 遇到 console.log('i') 打印 i
// 3.3 遇到 process.nextTick 加入到微任务队列
// 3.4 遇到 Promise 将同步任务 k 打印
// 3.5 将 Promise.then 加入到微任务队列

// 打印情况: a、g、f、h、b、d、c、e、i、k
// 宏任务( 由底到高 ):空
// 微任务( 由底到高 ):process.nextTick (P28)、Promise.then (P35)

// 3.6 查找微任务队列,由下开始执行
// 3.7 执行 process.nextTick, 打印 j
// 3.8 执行 Promise.then, 打印 l
// 打印情况: a、g、f、h、b、d、c、e、i、k、j、l
posted @ 2022-10-16 01:47  如是。  阅读(594)  评论(0编辑  收藏  举报