javascript 异步小结 (microtask 和 macrotask)

一、javascript 引擎的执行机制

javascript 是运行在浏览器上的单线程脚本语言, 它本身为了操作DOM而设计的,所以不允许多个线程同时进行,否则浏览器会不知道究竟该如何执行。

而根据js引擎自上而下依次执行的执行顺序,当一段代码解析时间过长时,就会产生阻塞,导致接下来的代码无法如期执行,会出现卡死的状态,用户体验非常不好,如果是因为CPU占用太多导致的卡死也就罢了,关键是CPU并没有很忙,所以js有了异步加载

我们先来看一下js的运行机制:

  • event loop 会自上向下执行,遇到异步任务时会将它放放 event table 中,主线程继续执行
  • event table会为这个异步任务注册函数,当满足触发条件时再将它推到event queue中
  • 当主线程执行完毕后,会到event queue中查看是否有异步任务,如果有,就会被推到主线程中继续执行

怎么样,思路是不是清晰啦,那我们来看一个简单的例子吧

console.log(1)

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

setTimeout((() => {
    console.log(2)
    return ()=>{
        console.log(3)
    }
})(), 100);

// 1 2 4 3

这段代码的执行顺序是:1,2,4,3。首先执行1都没有疑问了,然后event loop判断4是异步任务,把它放到event talbe中,继续执行,执行到2的时候,event loop判断2是一个立即执行函数,所以2会在主线程中执行,而return中是返回了一个异步任务,所以3也会放在event table中,这样主线程就执行完了,然后event loop 会去 event queue中查看任务队列,(这里要给大家说一下,当setTimeout 第二个参数为零的时候,会自动改为20),所以当2微秒后,setTimeout会放到 event queue中,event loop 将它推到主线程中执行,3在1毫秒结束后,也会被推到event queue中,当4执行完之后,event loop 会再监听 event queue,发现有3,再被推到event queue中,就这样反复地循环,直到程序执行完毕。

二、microtask 和 macrotask

任务队列就是我们刚刚说的 event queue 它并不是一个单独体,而是被分为两个部分 microtask 和macrotask:

macrotask(宏任务队列 )

  1. setTimeout
  2. setInterval
  3. setImmediate
  4. requestAnimationFrame
  5. I/O
  6. UI rendering

microtask(微任务队列 )

  1. process.nextTick
  2. Promise
  3. Object.observe
  4. MutationObserver

任务队列的执行顺序是:开始 -> 取task queue第一个task执行 -> 取microtask全部任务依次执行 -> 取task queue下一个任务执行 -> 再次取出microtask全部任务执行 ->

for( var i = 0; i < 10; i++){
    setTimeout(() => {
        console.log('i1=',i)
    }, 0);
    
    setTimeout((() => {
        console.log('i2=',i)
        return ()=>{
            console.log(3)
        }
    })(), 100);
}

new Promise((resolve, reject)=>{
    console.log(5)
    for(let i = 0; i<= 10000; i++){
        i ===10000 && resolve()
    }
}).then(()=>{
    console.log(6)
})
// 1
// 0 1 2 3 4 5 6 7 8 9
// 5 
// 6
// 10 10 10 10 10 10 10 10 10
// 3 3 3 3 3 3 3 3 3 3 3 

解释一下:event loop 会先执行1,当遇到for循环中 i1 是 会被放到宏任务队列中,再次执行,遇到 i2时,判断它是一个立即执行函数,会被放在主任务中执行,也就是依次执行,i3 作为一个返回函数100毫秒后被放到宏任务中,执行到promise时,event loop会执行函数体5,而执行到 resolve时,判断它也是一个异步任务,被放在微任务队列中,这样主线程执行完毕。
event loop空闲了,就首先执行微任务中的5,(如果微任务中有宏任务,会再被加入到宏任务中,执行宏任务,再执行微任务。),当微队列所有任务执行完毕后,会到宏任务中执行i1,在1毫秒后,再执行 3。

有没有感到很晕呀~哈哈哈哈,那就平时多练习吧!再给你们一个例子:

for( var i = 0; i < 10; i++){
    setTimeout(() => {
        console.log('i1=',i)
        new Promise((resolve,reject)=>{
            resolve()
        }).then(()=>{
            console.log(8)
        })
    }, 0);
}
//1
// i1= 10
// 8
// i1= 10
// 8
// i1= 10
// 8
// i1= 10
// 8
// i1= 10
// 8
// i1= 10
// 8
// i1= 10
// 8
// i1= 10
// 8
// i1= 10
// 8
// i1= 10
// 8

在我们知道 js是先执行microtask 后执行 macrotask的 ,那为什么还会这样打印呢?其实刚刚我们已经说过了 js的执行机制是 开始 -> 取task queue第一个task执行 -> 取microtask全部任务依次执行 -> 取task queue下一个任务执行 -> 再次取出microtask全部任务执行

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值