Author:
bugall
Wechat:bugallF
Email:769088641@qq.com
Github: https://github.com/bugallgit
虽然咱们用Javascript老是能够实现一些异步代码, 可是Javascript中真正的异步概念,可是直到ES6,Javascript才内建了直接的异步概念。github
对于原有Javascript引擎来讲, 它只关心如何去执行给定的代码块, 对于何时该执行哪些代码块这个是引擎不关心的。引擎是依赖于宿主环境的,这里的宿主环境并非指操做系统环境,由于不一样的平台提供的“可执行环境”不一样。而宿主环境就是为了隔离代码、语言与具体的平台而提出的一个设计。好比web浏览器环境,Node.js这样的工具等,全部的这些环境都有一个共同点,它们都提供了一种机制来处理程序中多个代码块的执行,且执行每块时调用Javascript引擎,这种机制被成为事件循环
。web
换句话说,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会被取出执行。code
若是你的事件循环中已经有不少项目后,定时器的回调就要被放到队尾( 不支持抢占式 )等待被执行,这也就是定时器不许的缘由。定时器的回调函数的执行要根据时间队列的状态而定。
那么该如何去下降定时器偏差呢?
严格来讲,定时器并不直接把回调函数直接插到事件循环队列,定时器会在有机会的时候插入事件,对于连续的两个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的任务队列被执行