0
点赞
收藏
分享

微信扫一扫

记录 JavaScript 事件循环 Event Loop

萧让听雪 2022-03-12 阅读 101

1. JavaScript 是一门单线程语言

JavaScript 是一门单线程语言,所谓"单线程",就是指一次只能完成一件任务。如果有多个任务,就必须排队,前面一个任务完成,再执行后面一个任务,以此类推。

虽然JavaScript是单线程运行的,但是还是存在其他线程的;例如:处理Ajax请求的线程、定时器的线程、读写文件的线程(node.js中)等。

2. 同步任务和异步任务

"同步任务"就是指在主线程上排队执行的任务,只有前一个任务执行完毕,才能继续执行下一个任务,程序的执行顺序与任务的排列顺序是一致的、同步的。当我们打开网站时,网站的渲染过程,比如元素的渲染,其实就是一个同步任务。

"异步任务"是指不进入主线程,而进入任务队列的任务,只有任务队列通知主线程,某个异步任务可以执行了,该任务才会进入主线程,当我们打开网站时,像图片的加载,音乐的加载,其实就是一个异步任务。

3. Event Loop(事件循环)

  1. 所有任务都在主线程上执行,形成一个执行栈

  2. 主线程发现有异步任务,就在“任务队列”之中加入一个任务事件。

  3. 一旦“执行栈”中的所有同步任务执行完毕,系统就会读取“任务队列”(先进先出原则)。那些对应的异步任务,结束等待状态,进入执行栈并开始执行

  4. 主线程不断重复上面的第三步,这样的一个循环称为事件循环

4. 宏任务与微任务

如果任务队列中有多个异步任务,那么先执行哪个任务呢?

于是在异步任务中,也进行了等级划分,分为宏任务(macrotask)微任务(microtask)

不同的API注册的任务会依次进入自身对应的队列中,然后等待事件循环将它们依次压入执行栈中执行。

4.1 宏任务包括:

  • 整体的JavaScript代码
  • setTimeout, setInterval, setImmediate,
  • I/O
  • UI rendering

4.2 微任务包括:

  • process.nextTick
  • Promise 就先记住这个!!
  • Object.observe(已废弃)
  • MutationObserver(html5新特性)

4.3 运行机制

在事件循环中关键步骤如下:

  • 执行一个宏任务(栈中没有就从事件队列中获取)。
  • 执行过程中如果遇到微任务,就将它添加到微任务的任务队列中。
  • 宏任务执行完毕后,立即执行当前微任务队列中的所有微任务(依次执行,一轮事件循环结束)
  • 开始下一个宏任务。
    在这里插入图片描述

在这里插入图片描述

5. 例题

console.log('start')

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

new Promise(function(resolve) {
    console.log('promise');
    //注意这边调用resolve
    //不然then方法不会执行
    resolve()
}).then(function() {
    console.log('then');
})

console.log('end');
  1. 刚开始打印start
  2. 遇到setTimeout放入宏任务中,等待执行
  3. 遇到new Promise的回调函数,同步执行,打印promise
  4. resolve后,then方法会放入微任务,等待执行
  5. 打印end这时整个执行栈清空了,宏任务和微任务队列各有一个回调方法
  6. 先执行微任务队列,打印then
  7. 执行宏任务,打印timeout
async function async1(){
    console.log('async1 start')
    await async2()
    console.log('async1 end')
}

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

console.log('script start')

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

async1()

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

console.log('script end')
  1. 第一轮循环开始
  2. 打印script start
  3. 发现setTimeout放入宏任务1
  4. 打印async1 start
  5. 打印async2
  6. await async2函数后面的回调放入微任务1
  7. 打印promise1
  8. then中的函数放入微任务2
  9. 打印script end
  10. 调用栈清空,开始执行微任务1,打印async1 end
  11. 执行微任务2,打印promise2
  12. 微任务执行完,第一轮循环结束
  13. 开始宏任务1,打印setTimeOut
  14. 结束。
举报

相关推荐

0 条评论