• 浏览器和nodejs中的eventloop


    浏览器和nodejs中的eventloop

    浏览器中的Event Loop

    在浏览器中,设计成为了单线程。如果要处理异步请求,则需要增加一层调度逻辑,把js代码封装成一个个的任务,放在一个任务队列中,主线程不断的读取任务执行。每次调取任务,都会创建新的调用栈

    • 宏任务: setTimeout,setInterval,requestAnimationFrarme,Ajax,fetch,script
    • 微任务: Promise.then,MutationObserver,Object.observe
      等微任务执行才能执行宏任务

    Node.js中的Eventloop

    nodejs是一个新的js运行环境。同时要支持异步逻辑,定时器,io,网络请求
    Nodejs任务宏任务之间是有优先级的,定时器的Timer的逻辑比Io的逻辑高,close的优先级就很低。

    • 优先级: Timers, Pending, poll, check,close
    • Timers Callback:涉及到时间,越早执行越好
    • Pending Callback: 处理网络,io等异常的回调
    • Poll Callback: 处理io的data,网络的connection
    • check Callback: 执行setImmediate的回调,特点是刚执行完io之后就能回调这个
    • Close Callback: 关闭资源的回调,晚点执行也不影响
      Nodejs中的EventLoop每次是把当前优先级的所有宏任务跑完再去跑微任务,然后再去跑下一个优先级的宏任务

    Node.js中的EventLoop的完整流程

    • Timer阶段: 执行一定数量的定时器,太多的话留到下次执行 setTimeout setInterval
    • 微任务: 执行所有的nextTick的微任务。再去执行其他的普通微任务
    • Pending阶段(I/Ocallback阶段): 执行一定数量的io和网络的异常回调,太多的话留到下次执行。处理一些上一轮循环中的少数未执行的io回调
    • 微任务: 执行所有nextTick的微任务,在执行其他的普通微任务
    • Idle/Prepare阶段: 内部的一个阶段
    • 微任务: 执行所有的nextTick微任务,然后执行其他的普通微任务
    • Poll阶段: 执行一定数量的文件的data回调,网络的connection回调,太多的放到下次执行,如果没有io回调并且没有timers,check阶段的回调处理,就阻塞在这里等待io时间
    • 微任务: 执行一定数量的setImmediate的callback,太多的留到下次执行
    • check阶段: 执行一定数量的setImmmediate的callback,太多的留到下次执行
    • 微任务: 执行一定数量的nextTick的微任务,在执行其他的普通微任务
    • close阶段: 执行一定数量的close事件的callback,太多的话留到下次执行
    • 微任务: 执行所有的nextTick的微任务,在执行其他的普通微任务
      如果执行到poll阶段,发现poll队列为空并且timers队列,check队列都没有执行,那么就阻塞在这里等待io事件
      在这里插入图片描述
    1. timer
      timers阶段会执行setTimeout和setInterval回调,最初是由poll阶段控制的,在node中的定时器也不是准确的事件,只能是尽快执行
    2. poll
      • 回到timer阶段执行回调
      • 执行io回调
      如果poll队列不为空,会遍历回调队列并同步执行,直到队列为空或者达到系统限制
      如果poll队列为空
          如果由setImmediate回调要执行,poll阶段会停止,然后进入到check阶段执行回调
          如果没有setImmediate回调需要执行,会等待回调并加入到队列中立即执行回调,需要由超时时间,防止一直等待下去
      
      • 1
      • 2
      • 3
      • 4
      当设定了timer而且poll队列为空,会判断timer是否超时,如果有的话timer阶段执行回调
    3. check阶段
      setImmediate()的回调会被加入到check队列中。
      console.log('start')
      setTimeout(() => {
          console.log('timer1')
          Promise.resolve().then(function() {
              console.log('promise1')
          })
      }, 0)
      setTimeout(() => {
          console.log('time2')
          Promise.resolve().then(function() {
              console.log('promise2')
          })
      }, 0)
      Promise.resolve().then(function() {
          console.log('promise3')
      })
      console.log('end')
      // start => end => promise3 => 
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      一开始执行栈的同步任务,执行完毕后,打印出start end,讲两个timer放入timer队列,执行微任务,打印出promise3
      进入timers阶段,执行timer1的回调函数,打印timer1,然后Promise.then放入微任务队列,然后执行timer2,打印timer2,将promise.then()放入微任务队列。timer阶段有几个setTimeout/setInterval都会依次执行,并不像浏览器端,没执行一个宏任务之后就在去执行一个微任务。
    4. 注意
      setImmediate设计在poll阶段完成时执行,即check阶段
      setTimeout设置在poll阶段为空闲的时候,而且达到设定时间之后才会执行。但是在timer阶段执行
      在异步io内部调用的时候,总是先执行setImmediate,在执行setTimeout
      process.nextTick,这个函数独立于event loop之外,有一个自己的队列,当每个阶段完成后,如果存在nextTick队列,就会清空队列中的所有回调函数,并且由于其他的微任务队列先执行
      javascript最早就是用来写网页交互的逻辑,为了避免多线程同时修改dom的同步问题,所以被设计成为了单线程,解决了单线程的阻塞问题,加了一层调度逻辑,就是loop循环和task队列二,阻塞的线程放到其他的线程跑,支持了异步,为了支持高优先级的任务调度,引入了微任务队列,
  • 相关阅读:
    【漏洞复现-solr-命令执行】vulfocus/solr-cve_2019_17558
    设计模式存在哪些关联关系,六种关系傻傻分不清--- UML图示详解
    DataFrame创建介绍_大数据培训
    多线程死锁案例
    迅为RK3399开发板创建android工程
    第一章 visual studio下载安装
    Day4 数据分析 Excel图表【零基础】
    软件工程综合实践课程第一周作业(面向对象编程实验与继承与多态实验)
    Linux基础教程:5、shell入门前的准备
    【Leetcode】岛屿问题——dfs算法
  • 原文地址:https://blog.csdn.net/m0_73280507/article/details/138166467