一文读懂JS同异步与事件循环

1. Event Loop (事件循环)

1.1 Event Queue (任务队列)

所有的任务可以分为同步任务和异步任务,同步任务,顾名思义,就是立即执行的任务,同步任务一般会直接进入到主线程中执行;而异步任务,就是异步执行的任务,比如ajax网络请求,setTimeout 定时函数等都属于异步任务,异步任务会通过Event Queue (任务队列)的机制来进行协调。

同步和异步任务分别进入不同的执行环境,同步的进入主线程,即主执行栈,异步的进入 Event Queue (任务队列) 。主线程内的任务执行完毕为空,会去 Event Queue (任务队列) 读取对应的任务,推入主线程执行。 上述过程的不断重复就是我们说的 Event Loop (事件循环)

  • 看个简单的例子
console.log('代码开始执行');
setTimeout(function(){
    console.log('定时器开始执行')
});
console.log('代码执行结束');
//结果是: 代码开始执行->代码执行结束->定时器开始啦
//因为setTimeout是异步任务,所以就会后后执行,
  • 在看一个例子
setTimeout(function(){
    console.log('2')
});
for(let i =0;i<3000;i++){
    console.log(1);
}
console.log(3);
//输出3000个1之后,在输出3,然后才输出2
//这也就说明先执行同步任务,等到同步任务执行完后,然后再到任务队列中去执行异步任务

1.2 Macro Task (宏任务)和 Micro Task(微任务)

任务队列还可以分成宏任务微任务微任务就是一个跟屁虫,一直跟在当前宏任务后面,代码执行到一个微任务,一个接着一个。

我们常见的宏任务有:script(整体代码)、setTimeout、setInterval、I/O、UI 交互事件、setImmediate(Node.js 环境),ajax,读取文件
微任务有:Promise、MutationObserver、process.nextTick(Node.js 环境);
优先级 process.nextTick > promise.then > setTimeout > setImmediate

2. 经典例题


      console.log('script start')

      async function async1 () {
        console.log('async1'); //同步
        await async2()
        console.log('async1 end') //异步
      }
      async function async2 () {
        console.log('async2 end')
      }
      
      async1()
      
      setTimeout(function () {
        console.log('setTimeout')
      }, 0)
      
      process.nextTick(() => {
        console.error('nextTick')
      })
      
      new Promise(resolve => {
        console.log('Promise') //同步
        resolve()
      }).then(function () {
        console.log('promise1')
      }).then(function () {
        console.log('promise2')
      })

      console.log('script end')
      

首先执行同步代码,输出script start
调用async1 输出async1
async1内同步调用async2 输出async2 end 并产生微任务1
遇到setTimeout,产生一个宏任务
遇到process.nextTick 产生 微任务2
执行Promise,输出Promise .then产生 微任务3微任务4
接着输出script end
同步任务都执行完了,然后去到异步任务队列 (此时会输出一个无运行结果的undefined)
开始执行当前宏任务产生的微任务队列
微任务优先级process.nextTick最高 所以微任务执行顺序为2134
微任务2 process.nextTick的nextTick
微任务1 async1 end
微任务3 promise1
微任务4 promise2
最后,执行下一个宏任务,即执行setTimeout,输出setTimeout

  • node环境输出结果:

     script start
     async1
     async2 end
     Promise
     script end
     *undefined*
     > nextTick
     async1 end
     promise1
     promise2
     setTimeout
    

3. 总结

  • 首先执行同步代码,
  • 当执行完所有的同步代码后,执行栈为空,去查询是否有异步代码需要执行
  • 执行所有的微任务
  • 当执行完所有的微任务后
  • 开始下一轮Event-loop,执行宏任务中的异步代码

最后的最后,记住,JavaScript 是一门单线程语言,异步操作都是放到事件循环队列里面,等待主执行栈来执行的,并没有专门的异步执行线程。

参考:
深入理解JavaScript事件循环机制 - ChessZhang
聊聊事件循环机制 - 柒天后

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值