javascript事件循环(浏览器端、node端)

  • 时间:2018-12-02 23:15 作者:iamswf 来源:iamswf 阅读:606
  • 扫一扫,手机访问
摘要:为什么要理解js中的事件循环javascript是一种基于事件的单线程、异步、非阻塞编程语言,我经常在看书或者者浏览别人博客的时候看到这种说法,可是之前一直没有深入了解过,只知道javascript中经常使用各种回调函数,比方浏览器端的各种事件回调(点击事件、滚动事件等)、ajax请求回调、setTi

为什么要理解js中的事件循环

javascript是一种基于事件的单线程、异步、非阻塞编程语言,我经常在看书或者者浏览别人博客的时候看到这种说法,可是之前一直没有深入了解过,只知道javascript中经常使用各种回调函数,比方浏览器端的各种事件回调(点击事件、滚动事件等)、ajax请求回调、setTimeout回调以及react16的内核fiber中用到的requestAnimationFramepromise回调、nodefs板块异步读取文件内容回调、process板块的nextTick等等。最近有时间浏览了各种资料(后面有各种相关资料的链接),终于明白所有的这些内容其实都离不开javascript事件循环,而浏览器端的事件循环又与node端的事件循环有较大区别,下面分别详情下。

浏览器 vs node

自从有了nodejavascript既可以运行在浏览器端又可以运行在服务端,以chrome浏览器为例,相同点是都基于v8引擎,不同的是浏览器端实现了页面渲染、而node端则提供了少量服务端会用到的特性,比方fsprocess等板块,同时node端为了实现跨平台,底层使用libuv来兼容linuxwindowsmacOS三大操作系统。因而尽管都实现了javascript的异步、非阻塞特性,但是却有有不少不同之处。

浏览器端

无论是在浏览器端还是node端,线程入口都是一个javascript脚本文件,整个脚本文件从第一行开始到最后运行完成可以看作是一个entry task,即初始化任务,下图task中第一项即为该过程。初始化过程中一定会注册不少异步事件,比方常见的setTimeoutonClickpromise等,这些异步事件执行中又有可能注册更多异步事件。所有的这些异步任务都是在事件循环一次次的循环中得到执行,而这些异步任务又可以分为两大类,即microtasktask(或者macrotask)。那么一次事件循环中会执行多少个异步任务?microtasktask的执行先后顺序是什么呢?看下图。

先忽略图中的红色部分(渲染过程,后面再详情),顺时针方向即为事件循环方向,可以看出每次循环会先后执行两类任务,taskmicrotask每一类任务都由一个队列组成,其中task主要包括如下几类任务:

  1. index.js(entry)
  2. setTimeout
  3. setInterval
  4. 网络I/O

microtask主要包括:

  1. promise
  2. MutationObserver

因而microtask的执行事件结点是在两次task执行间隙。前面说了,每类任务都由一个队列组成,这其实是一种生产者-消费者模型,事件的注册过程即为任务生产过程,任务的执行过程即为事件的消费过程。那么每次轮到一类任务执行各个队列会出队多少个任务来执行呢?图中我已经标明,task队列每次出队一项任务来执行,执行完成之后开始执行microtask;而microtask则每次都把所有(包括当前microtask执行过程中新增的任务)任务执行完成,而后才会继续执行task。也就是说,即使microtask是异步任务,也不能无节制的注册,否则会阻塞task页面渲染的执行。比方,下面的这段代码中的setTimeout回调任务将永远得不到执行(注意,谨慎运行这段代码,浏览器可能卡死):

setTimeout(() => {    console.log('run setTimout callback');}, 0);function promiseLoop() {    console.log('run promise callback');    return Promise.resolve().then(() => {        return promiseLoop();    });}promiseLoop();

现在回过头来再看上图中的粉红色虚线部分,该过程表示的是浏览器渲染过程,比方dom元素的stylelayout以及position这些渲染,那为什么用虚线表示呢?是由于该部分的调度是由浏览器控制,而且是以60HZ的频率调度,之所以是60HZ是为了能满足人眼视觉效果的同时尽量低频的调度,假如浏览器一刻不停的频繁渲染,那么不仅人眼观察不到界面的变化效果(就好像夏天电扇转太快人眼分辨不出来),而且耗费计算资源。因而上图中渲染过程用虚线表示不肯定每次事件循环都会执行渲染过程。
仔细看虚线框起来的渲染过程,可以看到在执行渲染之前可以执行一个回调函数requestAnimationFrame。使用该钩子函数注册的回调函数同task回调和microtask回调一样,会进入专属的事件队列,但是requestAnimationFramesetTimeout不一样,不是为了在4ms,16ms或者1s之后再执行,而是在下一次页面渲染阶段去执行,具体来说是在stylelayout计算之前执行,因而requestAnimationFramesetTimeout更适合做动画,这里有个例子可以参考:http://jsfiddle.net/H7EEE/245/。效果如下图所示,可以看出requestAnimationFramesetTimeout动画效果更加流畅,react16的内核fiber也使用了该钩子函数来实现更加精细的任务调度算法,以提升前台页面性能。

现在总结一下浏览器端的事件队列,共包括三个事件队列:task队列、requestAnimationFrame队列以及microtask队列,javascript脚本加载完成后首先执行第一个task队列任务,即初始化任务,而后执行所有microtask队列任务,接着再次执行第二个task队列任务,以此类推,这其中穿插着60HZ渲染过程。先执行谁后执行谁现在理解清楚了,可是到每个事件队列执行的轮次时,分别会有多少个事件出队执行呢?答案见下图(截图自Jake Archibald大神的JSConf演讲视频):


可以看出,在一次事件循环中:普通task每次出队一项回调函数去执行,requestAnimationFrame每次出队所有当前队列的回调函数去执行,microtask每次出队所有当前队列的回调函数以及自己轮次执行过程中又新添加到队尾的回调函数。这三种不同的调度方式正好覆盖了所有场景。

实践一下

TODO:用前面详情的模型解释少量demo

node端

前面详情了下浏览器端的事件循环,涉及到taskmicrotask,其实node端的异步任务也包括这些,只不过node端的task划分的更细,如下图所示,node端的task可以分为4类任务队列:

  1. index.js(entry)、setTimeoutsetInterval
  2. 网络I/O、fs(disk)child_process
  3. setImmediate
  4. close事件

microtask包括:

  1. process.nextTick
  2. promise

nodetask分为了4类队列,每次事件循环会依次执行该4类事件队列。而每执行完一个task队列就会接着执行microtask队列,而后再接着执行下一个task队列。因而microtask队列的执行是穿插在各个类形的task之间的。
node端与浏览器端事件循环的一个很重要的不同点是,浏览器task队列每轮事件循环仅出队一个回调函数去执行接着去执行microtask,而node端只需轮到执行task,则会跟执行完队列中的所有当前任务,但是当前轮次新增到队尾的任务则会等到下一轮次才会执行,该机制与浏览器端的requestAnimationFrame的调度机制时一样的。
总结一下node端的事件循环,共包括4类task事件队列与2类microtask事件队列,microtask穿插在task之间执行。task每次轮到执行会将当前队列中的所有回调函数出队执行,而microtask的调度机制则与浏览器端一样,每次轮到执行都会出队所有当前队列的回调函数以及自己轮次执行过程中又新添加到队尾的回调函数去执行。与浏览器端不一样的是node端的microtask包括process.nextTickpromise两类。

实践一下

TODO:用前面详情的模型解释少量demo

引用

本篇总结主要参考了如下资源,强烈推荐浏览阅读:

  1. Jake Archibald: In The Loop - JSConf.Asia 2018
  2. Philip Roberts: What the heck is the event loop anyway? - JSConf.EU 2014
  3. Everything You Need to Know About Node.js Event Loop - Bert Belder, IBM
  4. Event Loop and the Big Picture?—?NodeJS Event Loop Part 1
  5. Timers, Immediates and Process.nextTick— NodeJS Event Loop Part 2
  6. Promises, Next-Ticks and Immediates— NodeJS Event Loop Part 3
  7. Handling IO?—?NodeJS Event Loop Part 4
  8. Event Loop Best Practices?—?NodeJS Event Loop Part 5
  • 全部评论(0)
最新发布的资讯信息
【系统环境|】【Azure API Management】实目前API Management服务中使用MI(管理标识 Managed Identity)访问启用防火墙的Storage Account(2025-10-16 23:53)
【系统环境|】【Azure 存储服务】App Service 访问开启防火墙的存储账号时遇见 403 (This request is not authorized to perform this opera...(2025-10-16 23:52)
【系统环境|】YouTube账号购买全攻略|点击查看原文购买|购买YouTube账号、油管号自动发货服务的优势与缺点 附:YouTube无法加载?可能是这个设置在作怪!(2025-10-16 23:52)
【系统环境|】【Azure Developer】Azure Automation 自动化账号生成的时候怎么生成连接 与证书 (Connection & Certificate)(2025-10-16 23:41)
【系统环境|】【Azure Redis 缓存】如何使得Azure Redis可以仅从内网访问? Config 及 Timeout参数配置(2025-10-16 23:40)
【系统环境|】近日,四川宜宾,某地下停车场内的监控拍到了一幕惊险的画面。危险!一汽车修理师傅正在修理一辆3吨重的悍马时,悍马车突然自己启动,眼看就要撞向前面的一辆灰色轿车了,一侧的修理师傅做出了一个令人始料不...(2025-10-16 23:38)
【系统环境|】iOS 能耗监控与电池优化实战:如何查看App耗电量、分析CPU、GPU内存使用、(uni-app iOS开发性能调试指南)(2025-10-16 23:37)
【系统环境|】《风流女管家》法国犯罪悬疑片--《风流女管家》法语高清全集免费在线无广告完整观看---《风流女管家》HD正版免费播放-手机电脑皆可看(2025-10-16 23:36)
【系统环境|】2025含金量排名前十计算机专业证书(2025-10-15 20:51)
【系统环境|】你有白帽众测 我有黑帽雇佣(2025-10-15 20:50)
手机二维码手机访问领取大礼包
返回顶部