事件环

科技工作者之家 2020-11-17

在计算机领域,事件环,或者被称为消息分发器,消息环,消息泵或者运行环这些定义不过是一个程序结构体,用以在程序中等待,分发事件或者消息。它的工作方式是向内部或者外部的“事件提供方”发出请求(通常采取封锁请求的方式,直到有事件发生),然后再呼叫相应的事件处理器(又称“事件的分发“)。 事件环通常于编程设计模式” 反应器模式“相结合,前提是事件提供方遵循相同的文件接口, 这样事件提供方就可以被选择, '被轮询' (Unix系统这样用被动方式称呼,现在也可以直接叫 轮询). 事件环几乎总是对消息发出方进行异步操作。

当一个事件流被用作程序的中心控制流程, 事实上它通常做这个用途, 这时它又可以被称为”主环“或者”主事件环“。本文标题称为事件环贴切一点,因为这样的事件环一直是处在程序的最上的控制层面的。

介绍JavaScript的主要用途主要是用户互动,和操作DOM。如果JavaScript同时有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点1,这时这两个节点会有很大冲突,为了避免这个冲突,所以决定了它只能是单线程,否则会带来很复杂的同步问题。此外HTML5提出Web Worker标准,允许JavaScript脚本创建多个线程(UI线程, 异步HTTP请求线程, 定时触发器线程...),但是子线程完全受主线程控制,这个新标准并没有改变JavaScript单线程的本质。

任务队列

单线程一个一个完成任务,前一个任务完成了,才会执行下一个任务,就是排队一样,不能插队,只能前面的人完成才能轮到后一个。那么问题来了,加入一个人在那办理很多任务,一时半会办不完,难道就一直卡在那里吗,所有任务可以分成两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous)。

所有同步任务都在主线程上执行,形成一个执行栈

主线程之外,还存在一个任务队列。只要异步任务有了运行结果,就在任务队列之中放置一个事件。

一旦执行栈中的所有同步任务执行完毕,系统就会读取任务队列,看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。

主线程不断重复上面的第三步。

时间循环主线程从任务队列中读取事件,这个过程是循环不断的,所以整个的这种运行机制又称为Event Loop(事件循环)

function readi console. log(1) setTimeout(function (( console. log(2) console. log(3);reado分析:setTimeout()的就表示当前代码执行完(执行栈清空)以后,立即执行(0毫秒间隔)指定的回调函数。

除了广义的同步任务和异步任务,我们对任务有更精细的定义:

Macrotask (宏任务):

setImmediate:把回调函数放在事件队列的尾部

setTimeout:定时器

setInterval:定时器

Microtask 微任务):

process.nextTick:把回调函数放在当前执行栈的底部

Promise:

javascript事件环事件循环的顺序,决定js代码的执行顺序。进入整体代码(宏任务)后,开始第一次循环。接着执行所有的微任务。然后再次从宏任务开始,找到其中一个任务队列执行完毕,再执行所有的微任务。

JS是单线程的,也就是说任务需要一个接一个的按顺序执行,这是因为JS作为浏览器端的脚本语言其开始主要用途还是与用户互动和操作DOM,假如JS有两个线程,一个添加DOM一个删除DOM,这就势必会出现不可预期的后果,所以说还是单线程更适合,但是这种方式有一个弊端,就是必须要等待前一个程序执行完毕才执行下一个,所以将程序分为了两类:同步任务和异步任务。 在JS的执行栈中,同步任务进入主执行栈(也可以说主线程),而异步任务进入任务队列(TaskQueue)等待执行,任务队列可以理解成一个消息队列,I/O设备完成一件事,就向任务队列添加一个事件,一旦主执行栈中所有的同步任务执行完毕,就会读取任务队列中等待的任务,并放入执行栈开始执行,其实就是执行异步任务的回调函数,所以说异步任务必须指定回调函数,主线程会不断的循环这个动作,所以这种运行机制又称为EventLoop(事件循环)。

本词条内容贡献者为:

王伟 - 副教授 - 上海交通大学

科技工作者之家

科技工作者之家APP是专注科技人才,知识分享与人才交流的服务平台。