摘要:而事件循环是主线程中执行栈里的代码执行完毕之后,才开始执行的。由此产生的异步事件执行会作为任务队列挂在当前循环的末尾执行。在下,观察者基于监听事件的完成情况在下基于多线程创建。
主要问题:
1、JS引擎是单线程,如何完成事件循环的? 2、定时器函数为什么计时不准确? 3、回调与异步,有什么联系和不同? 4、ES6的事件循环有什么变化?Node中呢? 5、异步控制有什么难点?有什么解决方案?
二、事件队列循环
(一)、浏览器线程
JavaScript引擎是基于事件驱动单线程运行的,浏览器无论在什么时候都只且只有一个线程在运行JavaScript程序,等待着任务队列中任务的到来,然后加以处理。
浏览器的内核是多线程的,它们在内核控制下相互配合协作,一个浏览器至少实现三个常驻线程:JavaScript引擎线程,GUI渲染线程,浏览器事件线程。
GUI渲染线程负责渲染浏览器界面,当界面需要重绘(Repaint)或由于某种操作引发回流(Reflow)时,该线程就会执行。GUI渲染线程与JavaScript引擎是互斥的,当JavaScript引擎执行时GUI线程会被挂起,GUI更新会被保存在一个队列中等到JavaScript引擎空闲时立即被执行。所以渲染操作的消耗特别大。
(一)、事件类型与队列
事件循环:引擎会创建一个类似于 while (true) 的无限循环,每执行一次循环体的过程称之为 Tick。每次 Tick 的过程就是查看是否有待处理事件,如果有则取出相关事件及回调函数放入执行栈中由主线程执行。待处理的事件会存储在一个任务队列中,也就是每次 Tick 会查看任务队列中是否有需要执行的任务。
任务队列:异步操作会将相关回调添加到任务队列中。而不同的异步操作添加到任务队列的时机也不同,如 onclick, setTimeout, ajax 处理的方式都不同,这些异步操作是由浏览器内核的 webcore 来执行的,webcore 包含上图中的3种 webAPI,分别是 DOM Binding、network、timer模块。
onclick 由浏览器内核的 DOM Binding 模块来处理,当事件触发的时候,回调函数会立即添加到任务队列中。
setTimeout 会由浏览器内核的 timer 模块来进行延时处理,当时间到达的时候,才会将回调函数添加到任务队列中。
js定时器不准确的原因: 1、异步函数的回调执行会阻塞下一个循环tick。2、浏览器的时间精度不一。
ajax 则会由浏览器内核的 network 模块来处理,在网络请求完成返回之后,才将回调添加到任务队列中。
主线程:JS 只有一个线程,称之为主线程。而事件循环是主线程中执行栈里的代码执行完毕之后,才开始执行的。所以,主线程中要执行的代码时间过长,会阻塞事件循环的执行,也就会阻塞异步操作的执行。只有当主线程中执行栈为空的时候(即同步代码执行完后),才会进行事件循环来观察要执行的事件回调,当事件循环检测到任务队列中有事件就取出相关回调放入执行栈中由主线程执行。
在这个过程提高页面响应速度的方式:减少主线程同步代码的数量, 将不重要的代码转移到事件循环阶段执行
(三)、ES6的任务队列
对事件队列进行了改造,使得异步回调可以更早的执行,每个tick间隙都会优先执行任务队列,不用排到事件队列的末尾。一道必考题,看一下执行顺序:
Promise实例具有三种状态:等待,决议,拒绝。由此产生的异步事件执行会作为任务队列挂在当前tick循环的末尾执行。
(四)、Nodo中的事件循环
JS引擎的事件循环都需要宿主环境提供队列维护,Node脱离了浏览器,用到了不同的方式和底层系统做交互。
观察者:引擎在每个循环过程中询问观察者是否有要处理的事件。
在Window下,观察者基于IOCP监听事件的完成情况;在*nix下基于多线程创建。
在node增加了异步执行的api,如process。nextTick()和setImmiediate。
process。nextTick()的回调函数保存在一个数组中,setImmiediate则是保存在一个链表中。
在每轮循环中,会将nextTick的数组中回调函数全部执行完,然后执行一个setImmiediate链表中的回调。
(五)、异步事件的处理
异步事件的处理过程中有执行时间、顺序不确定,回调地狱,错误难捕获定位等问题。
1、jQuery的 Deferred队列模块
JQ在 Deferred队列模块的基础上,模拟实现了promise相似的方法。可以做到链式回调,延迟访问。
2、ES6 Promise
promise实例决议后就不可更改,解决了信任问题。可以使用链式回调,随时访问异步事件的状态。相比之前要将回调交给另一方控制,promise可以将回调的执行控制在自己的逻辑中。至此仍未从实质上解决异步。
3、Generator
生成器函数实现了真正的异步控制,可以切换执行环境,并在执行环境之间传递变量,实现协作的函数线程。
著名的co模块,结合使用Promise和Generator实现了异步流程的同步编码形式。
4、async 函数
ES2017 标准引入了 async 函数,使得异步操作变得更加方便。就是 Generator 函数的语法糖。
Node框架Koa采用了最新的async处理异步流程,使得编码更加简洁流畅。
推荐参考书:
《深入浅出NodeJS》 《jQuery技术内幕》 《Webkit技术内幕》 《你不知道的JavaScript》
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/107393.html
摘要:例如处理请求的线程处理事件的线程定时器线程读写文件的线程例如在中等等。事件循环事件循环是指主线程重复从消息队列中取消息执行的过程。事件触发时,表示异步任务完成,会将事件监听器函数封装成一条消息放到消息队列中,等待主线程执行。 一. 单线程 我们常说JavaScript是单线程的。 所谓单线程,是指在JS引擎中负责解释和执行JavaScript代码的线程只有一个。不妨叫它主线程。 但是实...
摘要:异步那些事一基础知识异步那些事二分布式事件异步那些事三异步那些事四异步那些事五异步脚本加载事件概念异步回调首先了讲讲中两个方法和定义和用法方法用于在指定的毫秒数后调用函数或计算表达式。功能在事件循环的下一次循环中调用回调函数。 JS异步那些事 一 (基础知识)JS异步那些事 二 (分布式事件)JS异步那些事 三 (Promise)JS异步那些事 四(HTML 5 Web Workers...
摘要:如果对语法分析和预编译,还有疑问引擎执行的过程的理解语法分析和预编译阶段。参与执行过程的线程分别是引擎线程也称为内核,负责解析执行脚本程序的主线程例如引擎。以上便是引擎执行宏任务的整个过程。 一、概述 js引擎执行过程主要分为三个阶段,分别是语法分析,预编译和执行阶段,上篇文章我们介绍了语法分析和预编译阶段,那么我们先做个简单概括,如下: 1、语法分析: 分别对加载完成的代码块进行语法...
摘要:如果对语法分析和预编译,还有疑问引擎执行的过程的理解语法分析和预编译阶段。参与执行过程的线程分别是引擎线程也称为内核,负责解析执行脚本程序的主线程例如引擎。以上便是引擎执行宏任务的整个过程。一、概述 js引擎执行过程主要分为三个阶段,分别是语法分析,预编译和执行阶段,上篇文章我们介绍了语法分析和预编译阶段,那么我们先做个简单概括,如下: 1、语法分析: 分别对加载完成的代码块进行语法检验,语...
摘要:一栈数据结构与不同,中并没有严格意义上区分栈内存与堆内存。引用数据类型的值是保存在堆内存中的对象。不允许直接访问堆内存中的位置,因此我们不能直接操作对象的堆内存空间。为了更好的搞懂变量对象与堆内存,我们可以结合以下例子与图解进行理解。 showImg(https://segmentfault.com/img/remote/1460000009784102?w=1240&h=683); ...
阅读 3484·2023-04-25 20:41
阅读 2659·2023-04-25 16:40
阅读 1432·2021-09-23 11:44
阅读 1251·2021-09-10 10:51
阅读 1681·2021-09-07 09:59
阅读 1642·2019-12-27 12:08
阅读 551·2019-08-30 15:44
阅读 3333·2019-08-30 11:08