最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 保姆级教程 | 一看就会的事件循环(EventLoop)大分析

    正文概述 掘金(一个装睡的人)   2021-06-26   519

    Todo List

    • 为什么js在浏览器中有事件循环的机制
    • 你了解事件循环中的两种任务么?
    • 为什么要引入微任务的概念,只有宏任务可以么?
    • 描述一下浏览器中事件循环的具体机制执行流程
    • Node中的事件循环和浏览器中的事件循环有什么区别
    • async / await
    • 做题
    • 进阶:伪代码实现

    为什么JS在浏览器中有事件循环的机制

    ?   one thread 单线程
       = one call stack 一个调用栈
       = one thing at a time 同一时间只能做一件事
    

    答:

    JS是单线程的,单线程意味着同一时间只能做一件事情,当前的事情没有做完时线程就会被挂起,造成后续任务的阻塞。 比如一些耗时的脚本下载,它不应该对用户后续的行为造成阻塞。于是就提出了异步任务(回调函数)的概念,将一些需要时间的事件交给处理异步任务的线程管理,不对主线程执行后续任务造成阻塞

    因为增加了多个线程并行之后,主线程需要知道其他线程的工作状态,“xx任务是否开始?xx任务是否执行完成?是否有异常情况?”等等。设想一下,如果有两个线程同时在操作一个dom节点,一个线程在删除dom节点,一个线程在操作dom节点,这两个操作就是互相冲突的,对于浏览器而言就会得不到我们想要的正确渲染结果。

    主线程需要频繁的和多个线程协调任务、调度任务,于是浏览器又进一步引入了事件循环(EventLoop)的机制,来协调多个线程多个事件之间的工作

    你了解事件循环中的两种任务么?

    ? 事件循环机制将异步任务分为了两种类型,宏任务 + 微任务
    

    答:

    宏任务:诸如 整个script各种事件回调(dom 事件、I/O)setTimeoutsetInterval 等任务。

    微任务:诸如 Promise.[then/finally/catch]MutationObserver (dom变化监听/前端回溯) 等任务。

    为什么要引入微任务的概念,只有宏任务可以么?

    答:

    不可以,为了解决单线程同步阻塞问题,引入了异步任务,并有多个线程协作。又为了更好的调度任务、协调多个线程之间的工作,引入了事件循环机制。

    因为将耗时的任务以异步任务的方式交给了其他线程处理,而为了保证多线程之间的有序工作,事件循环机制下只有主线程执行完调用栈内当前的所有同步任务,才会去询问有哪些异步任务可以传回主线程执行。

    也就是说无论你的异步任务实际耗不耗时,也一定至少是下一轮事件循环(一个用来调度主线程与其他线程之间任务的机制),它的回调函数才会被推入调用栈执行。而这样就会影响到需要尽快处理的那些事件,因为你不知道你前面还排着多少异步任务回调未被推入调用栈。于是就此引入了微任务的概念,去处理异步任务里追求时效性、更高优先级的事情。

    微任务的回调函数会在当前次事件循环结束前被推入调用栈执行。也就是说虽然是同一轮遇到的宏任务和微任务,但是宏任务的回调们会被放到后续的事件循环中执行,而微任务的回调们会在这一次事件循环结束前被全部执行。

    描述一下浏览器中事件循环的具体机制执行流程

    答:

    [精简版]: 调用栈为空 -> 执行宏任务队列中最早的一个宏任务 x -> 执行 x 关联的微任务队列中的所有微任务 -> 调用栈为空 -> .... (重复如上动作) 这样重复的轮询机制,被称之为事件循环。

    [叙述版]:

    理解代码运行过程

    加载JS,遇到任务(函数调用)会被推入调用栈,调用栈内的任务由主线程执行,执行完成后出栈,调用栈追踪JS的整个执行过程。

    同步任务会直接在调用栈内完成执行,异步任务会在入栈后传递给其他线程处理,对应异步任务函数出栈。其他线程处理异步任务时,当这些任务满足了执行回调的条件,按照任务类型,分别塞入宏任务队列当前宏任务关联的微任务队列。等待被推入调用栈执行。

    代码如下:

    function test() {
        console.log(1)
    }
    
    console.log(2)
    
    test()
    

    执行过程用简单的动画模拟了一下:

    保姆级教程 | 一看就会的事件循环(EventLoop)大分析

    多线程之间的资源协调、任务调度

    事件循环会持续不断的去监听调用栈,当调用栈空闲时(无可执行的任务,调用栈里只有全局执行环境),会读取宏任务队列中最早的一个任务,推入调用栈执行(运行流程同 1)。

    当前宏任务内各任务(函数)均执行完成,即当前批宏任务结束前,读取当前宏任务关联的微任务队列中的任务,依次推入调用栈执行(运行流程同 1)。

    调用栈空闲,推入最早的一个宏任务 -> 宏任务内部任务执行完成 -> 关联微任务队列全部执行完毕,这一连动作都做完调用栈又回到了空闲状态,这就是一轮事件循环。当JS引擎监听到调用栈空闲,并且宏任务队列上还有可以执行的任务,就会开启新一轮的事件循环,就这样一直重复轮询

    保姆级教程 | 一看就会的事件循环(EventLoop)大分析

    保姆级教程 | 一看就会的事件循环(EventLoop)大分析

    保姆级教程 | 一看就会的事件循环(EventLoop)大分析

    保姆级教程 | 一看就会的事件循环(EventLoop)大分析

    保姆级教程 | 一看就会的事件循环(EventLoop)大分析

    保姆级教程 | 一看就会的事件循环(EventLoop)大分析

    保姆级教程 | 一看就会的事件循环(EventLoop)大分析

    保姆级教程 | 一看就会的事件循环(EventLoop)大分析

    保姆级教程 | 一看就会的事件循环(EventLoop)大分析

    保姆级教程 | 一看就会的事件循环(EventLoop)大分析

    保姆级教程 | 一看就会的事件循环(EventLoop)大分析

    Node中的事件循环和浏览器中的事件循环有什么区别

    Node v10及以前:node.js 将事件循环机制分为了6个阶段,根据宏任务的作用,会分到不同阶段处理。而和浏览器事件循环机制不相同的是 —— v10 之前的node.js,在每一个阶段的所有宏任务全执行完成后,才会去查询一次这个阶段宏任务相关的所有微任务,依次执行。

    Node v10以后:在 node.js v10 之后的事件循环机制和浏览器事件循环机制流程保持了统一,在一个宏任务内所有任务执行完成后(整个宏任务结束前),就会去执行与之相关联的所有微任务,然后再开始下一个宏任务。

    如下代码运行后结果:

    setTimeout(() => {
      console.log(1)
      Promise.resolve().then(() => console.log(2))
    }, 0)
    
    setTimeout(() => {
      console.log(3)
      Promise.resolve().then(() => console.log(4))
    }, 0)
    

    Node v10 及以前:1 -> 3 -> 2 -> 4
    Node v10 以后:1 -> 2 -> 3 -> 4

    做题

    async / await

    讲到主线程和调用栈,就有一个不得不提的内容 async / await

    async 函数会返回一个 Promise 对象,便于回调函数管理(支持链式)。

    await 是一个运算符,用于组成表达式,await xxx 的计算结果取决于 await 它等待的东西,也就是 xxx。如果它等待的不是一个 Promise,那么它的计算结果就是它等待的东西。

    await 等待的是一个 Promise 时,它会对当前 await 后面声明的代码进行阻塞,直到 Promise 返回后才会继续执行后续代码。如果 xxx 遇到了 error 且没有做异常捕获的话,那 await 之后的内容永远不会被执行。

    执行流遇到 await functionXX(): Promise<any> 时,因为发生了函数调用,所以functionXX 会被压入调用栈,执行流会进入到函数内部,将 return Promise 之外的代码执行一遍(同步任务),Promise相关的异步操作会交由浏览器执行。

    1. 如下代码运行后结果
    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. 如下代码运行后结果
    console.log(1)
    
    // 1s 延时
    setTimeout(() => {
      console.log(2)
    }, 1000)
    
    async function fn() {
      console.log(3)
      setTimeout(() => {
        console.log(4)
      }, 20)
      return Promise.reject()
    }
    
    async function run() {
      console.log(5)
      await fn()
      console.log(6)
    }
    
    run()
    
    //需要执行 150ms 左右
    for (let i = 0; i < 90000000; i++) {}
    
    setTimeout(() => {
      console.log(7)
    
      new Promise((resolve) => {
        console.log(8)
        resolve()
      }).then(() => {
        console.log(9)
      })
    }, 0)
    
    console.log(10)
    
    

    答:

    // 待补充
    
    1. 如下代码运行后结果
    function executor(resolve, reject) {
      let rand = Math.random()
      console.log(1)
      if (rand > 0.5) resolve()
      reject()
    }
    
    const p0 = new Promise(executor)
    
    const p1 = p0.then((_) => {
      console.log('success-0')
      return new Promise(executor)
    })
    
    const p2 = p1.then((_) => {
      console.log('success-1')
      return new Promise(executor)
    })
    
    p2.catch((_) => {
      console.log('error')
    })
    
    console.log(2)
    

    答:

    // 待补充
    

    结束语

    感谢阅读 ?????? / 感谢阅读 ?????? / 感谢阅读 ??????

    保姆级教程 | 一看就会的事件循环(EventLoop)大分析


    起源地下载网 » 保姆级教程 | 一看就会的事件循环(EventLoop)大分析

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元