资讯专栏INFORMATION COLUMN

事件循环与任务队列

SQC / 2113人阅读

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

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的任务队列被执行

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/86986.html

相关文章

  • JavaScript运行机制和事件循环

    摘要:主线程不断重复上面的三步,此过程也就是常说的事件循环。所以主线程代码执行时间过长,会阻塞事件循环的执行。参考资料这一次,彻底弄懂执行机制任务队列的顺序机制事件循环搞懂异步事件轮询与中的事件循环 1. 说明 读过本文章后,您能知道: JavaScript代码在浏览器中的执行机制和事件循环 面试中经常遇到的代码输出顺序问题 首先通过一段代码来验证你是否了解代码输出顺序,如果你不知道输出...

    Ververica 评论0 收藏0
  • 今天,我明白了JS事件循环机制

    摘要:而这些队列由的事件循环来搞定宏任务与微任务,在最新标准中,它们被分别称为与。我们梳理一下事件循环的执行机制循环首先从宏任务开始,遇到,生成执行上下文,开始进入执行栈,可执行代码入栈,依次执行代码,调用完成出栈。 写在前面 js是一门单线程的编程语言,也就是说js在处理任务的时候,所有任务只能在一个线程上排队被执行,那如果某一个任务耗时比较长呢?总不能等到它执行结束再去执行下一个。所以在...

    maochunguang 评论0 收藏0
  • 总结:JavaScript异步、事件循环消息队列、微任务任务

    摘要:单线程异步非阻塞然后,这又牵扯到了事件循环消息队列,还有微任务宏任务这些。此步的位置不确定某个时刻后,定时器触发线程通知事件触发线程,事件触发线程将回调函数加入消息队列队尾,等待引擎线程执行。 前言 Philip Roberts 在演讲 great talk at JSConf on the event loop 中说:要是用一句话来形容 JavaScript,我可能会这样: Java...

    qianfeng 评论0 收藏0
  • JavaScript单线程事件循环(Event Loop)那些事

    摘要:概述本篇主要介绍的运行机制单线程事件循环结论先在中利用运行至完成和非阻塞完成单线程下异步任务的处理就是先处理主模块主线程上的同步任务再处理异步任务异步任务使用事件循环机制完成调度涉及的内容有单线程事件循环同步执行异步执行定时器的事件循环开始 1.概述 本篇主要介绍JavaScript的运行机制:单线程事件循环(Event Loop). 结论先: 在JavaScript中, 利用运行至...

    Shisui 评论0 收藏0
  • 浅谈不同环境下的JavaScript执行机制 + 示例详解

    摘要:如果没有其他异步任务要处理比如到期的定时器,会一直停留在这个阶段,等待请求返回结果。执行的执行事件关闭请求的,例如事件循环的每一次循环都需要依次经过上述的阶段。因此,才会早于执行。 showImg(https://segmentfault.com/img/bVbnY76); 概念 同步任务(Synchronous) 在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务 ...

    wanghui 评论0 收藏0

发表评论

0条评论

SQC

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<