事件循环与任务队列

逆变盆景
• 阅读 6389

Author: bugall
Wechat: bugallF
Email: 769088641@qq.com
Github: https://github.com/bugall

一: 事件循环

虽然我们用Javascript总是可以实现一些异步代码, 但是Javascript中真正的异步概念,但是直到ES6,Javascript才内建了直接的异步概念。

对于原有Javascript引擎来说, 它只关心如何去执行给定的代码块, 对于什么时候该执行哪些代码块这个是引擎不关心的。引擎是依赖于宿主环境的,这里的宿主环境并不是指操作系统环境,因为不同的平台提供的“可执行环境”不同。而宿主环境就是为了隔离代码、语言与具体的平台而提出的一个设计。比如web浏览器环境,Node.js这样的工具等,所有的这些环境都有一个共同点,它们都提供了一种机制来处理程序中多个代码块的执行,且执行每块时调用Javascript引擎,这种机制被成为事件循环

换句话说,Javascript引擎本身并没有时间的概念,只是一个按需执行Javascript任意代码片段的环境。“事件”的调度总是由包含它的环境进行。

注意!!!,ES6后事件的管理方式有所改变。ES6本身解决的事件在哪里管理的问题,现在ES6精确指定了事件循环的工作细节,这就 意味着技术上将其纳如了Javascript引擎的势力范围,而不再由宿主环境管理,这个改变的一个主要原因是ES6中Promise的引入,这个技术要求事件循环队列的调度运行能够直接进行精细的控制

我们看下面的这段代码:

function task() {
    console.log("Hello Word");
}
setTimeout(task, 1000);

如果你在代码中设置一个计时器, 当计时器到达指定的时间后执行函数task, 当Javascript引擎执行到定时器的时候会通知宿主环境:“我要去做别的事情, 等1秒后就调用task函数,注意:是调用而不是执行)

我们用一段伪代码实现一个简单的事件循环

var eventList = [event1, event2, event3];
var event;
while(true) {
    if (eventList.length > 0 ) {
        event = eventList.pop();
    }
    run(event);
}

可以看到有一个用while循环实现的持续运行的循环,当event1, event2, event3都取出被执行一次后称为一轮,循环的每一轮称为一个tick,对于每一个tick而言,如果在队列中有等待的事件,那么就会从队列中取出下一个事件并执行,这些事件就是我们代码中写的回调函数。

需要注意的是,定时器比较特殊,setTimeout(task, 1000)并没有把回调函数挂在事件循环队列中,它所做的就是设置一个定时器,当定时器到时后,环境会把你的回调函数放在事件循环中,这样,在未来某个时刻的tick会被取出执行。

如果你的事件循环中已经有很多项目后,定时器的回调就要被放到队尾( 不支持抢占式 )等待被执行,这也就是定时器不准的原因。定时器的回调函数的执行要根据时间队列的状态而定。
那么该如何去降低定时器误差呢?

二:任务队列

严格来说,定时器并不直接把回调函数直接插到事件循环队列,定时器会在有机会的时候插入事件,对于连续的两个setTimeout(..., 0)调用不能保证会严格按照调用顺序处理,所以各种情况都会发生,比如定时器漂移,这类结果是不可预测的,在Node.js中可以用process.nextTick(...)。但是不能保证所有环境都能控制异步的顺序。

在ES6中,在事件循环队列上有个一个新的概念,那就是任务队列,这个概念给大家带来的最大影响可能就是Promise的异步特性

任务队列就是挂在时间循环队列的每个tick之后的一个队列,在事件循环的每个tick中,可能出现的异步动作不会导致一个完整的新事件添加到事件循环队列中,而会在当前tick的任务队列末尾添加一个任务。
一个任务可能引起更多任务被添加到同一个队列末尾,所以理论上说,任务循环可能无限循环,无法转移到下一个事件循环tick.

任务队列的概念,那么怎么减少定时器的误差?看代码:

function task() {
    console.log("Hello Word");
}
setTimeout(task, 1000);

如果现在时间队列中有100个等待被执行的任务,这时候task任务准备插入到事件队列。

没有引入任务队列前:
task会被插入到当前事件循环队列的末端,等待下次的tick被执行,那么这就需要等到当前的tick被执行完,那么这时候的timer延时就决定于100个等待执行的任务耗时。

引入任务队列之后:
直接插入到当前tick的任务队列被执行

点赞
收藏
评论区
推荐文章
待兔 待兔
1年前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
虾米大王 虾米大王
3年前
java代码037
code038.jsp邮箱地址验证<%Stringmailaddressrequest.getParameter("mailaddress");code039emailnewcode039(mailaddress);//cls001类if(email.checkEmail())out.println(mailaddress
Stella981 Stella981
4年前
React Hooks实现异步请求实例—useReducer、useContext和useEffect代替Redux方案
<blockquote本文是学习了2018年新鲜出炉的ReactHooks提案之后,针对<strong异步请求数据</strong写的一个案例。注意,本文假设了:<br1.你已经初步了解<codehooks</code的含义了,如果不了解还请移步<ahref"https://reactjs.org/docs/hooksintro.html
Wesley13 Wesley13
4年前
mysqlsla安装和使用介绍
<divid"cnblogs\_post\_body"class"blogpostbody"<p安装mysqlsla<br源码路径:https://github.com/danielnichter/hackmysql.com<br源码存放路径:/usr/local/src<br1、获取源码<br如果没有git命令,请先安装git<
Wesley13 Wesley13
4年前
VSCode配置FiraCode和更纱黑体字体
!(https://oscimg.oschina.net/oscnet/c7bb62d935ceb01d3b7fe176322e84ae00d.png)Fira Code下载到FiraCode字体的GitHub(https://www.oschina.net/action/GoToLink?urlhttps%
Stella981 Stella981
4年前
LocalDateTime计算时间差
<divclass"htmledit\_views"id"content\_views"<pLocalDateTime为java8的新特性之一<br</p<p<br</p<pLocalDateTime.now()获得当前时间<br</p<p</p<h5</h5<divstyle"marginleft
Stella981 Stella981
4年前
GitHub上最火爆!码代码不得不知的所有定律法则
\TOC\<br\\来源:\\\nusr/hackerlawszh\(https://github.com/nusr/hackerlawszh)<br这是\hackerlaws\(https://github.com/dwmkerr/hackerlaws)的中文翻译,对开发人员有用的定律,理论,原则和模式
Stella981 Stella981
4年前
JQuery中对option的添加、删除、取值
jQuery获取Select选择的Text和Value:1\.$("select\_id").change(function(){//code...});  //为Select添加事件,当选择其中一项时触发2\.varcheckText$("select\_id").find("option:selected").text();  
Wesley13 Wesley13
4年前
Java 并发编程
!file(https://img2018.cnblogs.com/blog/1411156/201908/14111562019083017335711525785135.jpg)<br/作者|加多<br/<br/关注阿里巴巴云原生公众号,后台回复关键字\\“并发”\\\\,即可参与送书抽奖!<br/
Stella981 Stella981
4年前
HTML5新增input标签属性
一.inputtype属性1<formaction""2邮箱<inputtype"email"name""id""<inputtype"submit"value"提交"<br/<br/3手机号码<inputtype"tel"name
Stella981 Stella981
4年前
Noark入门之异步事件
引入异步事件主要是为了各模块的解耦,每当完成一个动作时,向系统发布一个事件,由关心的模块自己监听处理,可选择同步处理,异步处理,延迟处理。何时发布事件,当其他模块关心此动作时<br比如获得道具时,任务系统模块要判定完成进度,BI模块需要上报等等都可以监听此事件,已达模块解耦0x00事件源一个实现xyz.noark.core.event