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

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

1、背景

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

2、同步任务与异步任务

  单线程就意味着,全部的任务都须要排队,当前一个任务结束时,才会执行后一个任务。若是前一个任务耗时很长,后一个任务就不得不一直等待。 若是是由于计算量大,CPU忙不过来倒也正常,可是不少时候CPU是闲着的,由于IO设备(输入输出设备)很慢(好比Ajax操做从网络读取数据),不得不等着结果出来,再往下执行。javascript

  这时主线程彻底能够无论IO设备,挂起处于等待中的任务,先运行排在后面的任务。等到IO设备返回告终果,再回过头,把挂起的任务继续执行下去。因而,全部任务能够分红两种:java

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

  在这里用一张图来讲明:面试

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

3、宏任务与微任务

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

  • 宏任务:总体代码、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对应的回调函数(多个宏任务的时候取队头),当即执行
  • 检查无可执行任务,结束

在理解时候须要将上面提到的两点结合起来(同步异步 + 宏观微观),才会造成机制。在代码运行时,能够想成是以代码片断来工做的oop


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,打印三次(按顺序分别是sync一、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
  • 结果为 sync一、promise、sync二、promise-Then、setTimeout、setTimeout-Promise、lastSetTimeout、promise-Timeout

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

面试相关:

  • 给题目,问结果输出
  • 运行机制的原理 - 描述基础
  • 运行机制的理解 - 实际运用
相关文章
相关标签/搜索