JavaScript在浏览器环境下的事件循环(Event Loop)

这里主要记录在日常中对知识的学习,通过结合笔记与自身理解的方式尝试写下总结
文章对细节可能不会一一介绍解释,内容仅作参考
复制代码

一、背景

  1. JavaScript是一门单线程语言
  2. 单线程所带来的任务执行方式

二、同步任务与异步任务

  单线程就意味着,所有的任务都需要排队,当前一个任务结束时,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就不得不一直等待。 如果是因为计算量大,CPU忙不过来倒也正常,但是很多时候CPU是闲着的,因为IO设备(输入输出设备)很慢(比如Ajax操作从网络读取数据),不得不等着结果出来,再往下执行。

  这时主线程完全可以不管IO设备,挂起处于等待中的任务,先运行排在后面的任务。等到IO设备返回了结果,再回过头,把挂起的任务继续执行下去。于是,所有任务可以分成两种:

  1. 同步任务
  2. 异步任务

  在这里用一张图来说明:

  • 同步和异步任务分别进入不同的执行"场所",同步的进入主线程,异步的进入Event Table
  • 当异步任务完成时,Event Table会将对应的回调移入Event Queue中
  • JS引擎会持续不断检查主线程执行栈是否为空,当主线程内的任务全部执行完毕后,会去Event Queue读取排头第一个,进入到主线程执行
  • 上述过程不断重复,形成事件循环(Event Loop)

三、宏任务与微任务

  除了广义的同步任务和异步任务,会对任务有更精细的定义:

  • 宏任务:整体代码、setTimeout、setInterval
  • 微任务:Promise

  不同类型的任务会进入对应的Event Queue,比如setTimeout和setInterval会进入相同的Event Queue

  第一次进入整体代码(宏任务)后,开始第一遍循环,在主线程任务全部执行完毕后,会先去读取所有的微任务进行执行,然后再到宏任务,而宏任务里面或许又包含着宏任务与微任务。以此不断循环执行

  用一张图说明:

  举个栗子

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

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

console.log('console')
复制代码
  • 这段代码会作为宏任务,进入主线程
  • 先遇到setTimeout,将其放入Event Table,完成后会将其回调函数注册并放入到宏任务Event Queue
  • 接下来遇到了Promise,new Promise立即执行,然后遇到console.log('promise'),立即执行。then函数放入微任务Event Queue。
  • 遇到console.log('console'),立即执行
  • 整体代码作为第一个宏任务执行结束,接着查看是否有微任务?我们发现了then在微任务Event Queue里面,执行
  • 微任务全部执行完毕,第一轮事件循环结束,开始第二轮循环。从宏任务Event Queue发现有setTimeout对应的回调函数(多个宏任务的时候取队头),立即执行
  • 检查无可执行任务,结束

在理解时候需要将上面提到的两点结合起来(同步异步 + 宏观微观),才会形成机制。在代码运行时,可以想成是以代码片段来工作的


19.4.11 补充:今天在刷文章的时候看到相关的题目,感觉可以加深理解一波

    console.log('sync1')
    
    setTimeout(function (){
        console.log('setTimeout')
    }, 0)
    
    var promise = new Promise(function(resolve, reject) {
        setTimeout(function() {
            console.log('setTimeout-Promise')
        }, 0)
        console.log('promise')
        resolve()
    })
    promise.then(() => {
        console.log('promise-Then')
        setTimeout(function() {
            console.log('promise-Timeout')
        }, 0)
    })
    
    setTimeout(function() {
        console.log('lastSetTimeout')
    }, 0)
    
    console.log('sync2')
复制代码
  • 首先我们看整体代码,是第一遍同步执行,是第一个宏任务。按顺序下来一共调用了三次setTimeout(Promise中的代码也是同步执行的),调用了一次resolve,打印三次(按顺序分别是sync1、promise、sync2)。所以它产生出三个宏任务、一个微任务。宏任务队列按顺序有setTimeout、setTimeout-Promise、lastSetTimeout / 微任务队列按顺序有promise-Then
  • 接下来,因为微任务队列不为空,先执行全部微任务。所以promise-Then被打印出来。然后又调用了一次setTimeout,所以promise-Timeout进入宏任务队列。此时宏任务队列按顺序为setTimeout、setTimeout-Promise、lastSetTimeout、promise-Timeout
  • 没有微任务了,执行第二个宏任务,打印setTimeout,没有再产生宏任务微任务,此时微任务队列为空,所以接着执行第三个宏任务
  • 同上,打印setTimeout-Promise
  • 同上,打印lastSetTimeout
  • 同上,打印promise-Timeout
  • 结果为 sync1、promise、sync2、promise-Then、setTimeout、setTimeout-Promise、lastSetTimeout、promise-Timeout

  • 直到ES6,js才真正内建有直接的异步概念(Promise的引入)
  • 在promise出现之前,js并没有异步,有异步的是寄主环境(文中指浏览器)
  • 微任务是js引擎内部的一种机制,而宏任务是寄主环境的一种机制
  • setTimeout在现设计中会有4ms的最小时间取值,就是最快也是4ms后才执行

面试相关:

  • 给题目,问结果输出
  • 运行机制的原理 - 描述基础
  • 运行机制的理解 - 实际运用

转载于:https://juejin.im/post/5cac971951882518b87e109c

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值