一文告诉你 Event Loop 是什么?
Event Loop 也叫做“事件循环”,它其实与 JavaScript 的运行机制有关。
JS初始设计
JavaScript 在设计之初便是单线程,程序运行时,只有一个线程存在,在特定的时候只能有特定的代码被执行。这和 JavaScript 的用途有关,它是一门浏览器脚本语言,通常是用来操作 DOM 的,如果是多线程,一个线程进行了删除 DOM 操作,另一个添加 DOM,此时该如何处理?所以 JavaScript 在设计之初便是单线程的。
虽然 HTML5 增加了 Web Work 可用来另开一个线程,但是该线程仍受主线程的控制,所以 JavaScript 的本质依然是单线程
线程和进程
进程和线程是操作系统中的概念,在操作系统中,一个任务就是一个进程,比如你在电脑上打开了一个浏览器来观看视频,便是打开了一个浏览器进程,此时又想记录视频中的重要信息,于是你打开了备忘录,这便是一个备忘录进程,系统会为每个进程分配它所需要的地址空间,数据,代码等系统资源。如果把一个进程看做一个小的车间,车间里有很多工人,有的负责操作机器,有的负责搬运材料,每个工人可以看做一个线程,线程可以共享进程的资源。可以说,线程是进程的最小单位,一个进程可以包含多个线程。
执行栈和任务队列
单线程的 JavaScript 一段一段地执行,前面的执行完了,再执行后面的,试想一个,如果前一个任务需要执行很久,比如接口请求、I/O 操作,此时后面的任务只能干巴巴地等待么?干等不仅浪费了资源,而且页面的交互程度也很差。JavaScript 意识到了这个问题,他们将任务分成了同步任务和异步任务,对于二者有不同的处理。
JavaScript 在运行时会将变量存放在堆(heap)和栈(stack)中,堆中通常存放着一些对象,而变量及对象的指针则存放在栈中。JavaScript 在执行时,同步任务会排好队,在主线程上按照顺序执行,前面的执行完了再执行后面的,排队的地方叫执行栈(execution context stack)。JavaScript 对异步任务不会停下来等待,而是将其挂起,继续执行执行栈中的同步任务,当异步任务有返回结果时,异步任务会加入与执行栈不一样的队列,即任务队列(task queue),所以任务队列中存放的是异步任务执行完成后的结果,通常是回调函数。
当执行栈的同步任务已经执行完成,此时主线程闲下来,它便会去查看任务队列是否有任务,如果有,主线程会将最先进入任务队列的任务加入到执行栈中执行,执行栈中的任务执行完了之后,主线程便又去任务队列中查看是否有任务可执行。主线程去任务队列读取任务到执行栈中去执行,这个过程是循环往复的,这便是 Event Loop,事件循环。
网上有张流传甚广的图对这一过程进行了总结,在图中我们可以看到,JavaScript 在运行时产生了堆和栈,ajax、setTimeout 等异步任务被挂起,异步任务的返回结果加入任务队列,主线程会循环往复地读取任务队列中的任务,加入执行栈中执行。
(JavaScript 运行机制,图片来源于网络)
宏任务与微任务
异步任务有更深一层的划分,它们是宏任务(macro task)和微任务(micro task),二者的执行顺序也有差别。在上面我们讲到异步任务的结果会进入任务队列中,对于不同的事件类型,宏任务会加入宏任务队列,微任务会加入微任务队列。在执行栈中的同步任务执行完成后,主线程会先查看任务队列中的微任务,如果有没有,则去宏任务队列中取出最前面的一个事件加入执行栈中执行;如果有,则将所有在微任务队列中的事件依次加入执行栈中执行,直到所有事件执行完成后,再去宏任务中取出最前面的一个事件加入执行栈,如此循环往复。
由此我们可以得出结论,主线程总是会先查看微任务队列,等到微任务队列中的事件都处理完成后,再去宏任务队列中添加一个事件到任务栈中执行。
常见的宏任务有 setTimeout,setInterval;常见的微任务有 new Promise。
代码例子体验
console.log(1) setTimeout(function() {
console.log(2) new Promise(function(resolve) {
console.log(3)
resolve(4)
}).then(function(num) {
console.log(num)
})
}, 300) new Promise(function(resolve) {
console.log(5)
resolve(6)
}).then(function(num) {
console.log(num)
}) setTimeout(function() {
console.log(7)
}, 400)
我们一步步来分析上面的执行顺序,当程序开始执行时,首先打印出 1,然后遇到了 setTimeout,主程序将它挂起,300 毫秒后它的回调函数进入宏任务队列,我们记做 setTimeout1。随后遇到了 new Promise,resolve 部分是同步执行的,所以会打印出 5,then 中的回调函数进入微任务队列,我们暂时记做 promise1。最后是 setTimeout,同理在 400 毫秒后加入了宏任务队列,我们记做 setTimeout2。
此时任务队列的情况如下:
宏任务 |
微任务 |
setTimeout1 |
promise1 |
setTimeout2 |
执行栈中的同步任务执行完成后,主线程查看任务队列时发现存在微任务,于是把 promise1 执行了,打印出 6。此时微任务队列已经空了,于是开始查看宏任务队列,将 setTimeout1 的回调函数加入任务栈开始执行,于是首先打印出 2,之后是 3,再将 then 中的回调函数加入微任务队列,我们记做 promise2。
此时任务队列的情况如下:
宏任务 |
微任务 |
setTimeout2 |
promise2 |
此时执行栈也空了,于是将微任务 promise2 加入执行栈,打印出 4。此时微任务已经执行完,再查看宏任务队列,于是执行 setTimeout2,打印出 7。
所以代码中的输出顺序是 1,5,6,2,3,4,7。
注意,主线程对微任务的读取是逐个读取,直到微任务队列为空,再读取宏队列,对宏任务队列的读取在一个循环中只读取一个。
小结
我们了解了 JavaScript 的运行机制,它是单线程的。JavaScript 中的任务可分为同步任务和异步任务,同步任务总是先进入执行栈中执行,异步任务会被挂起,直到有结果返回时,异步任务会进入任务队列中等待主线程读取执行。当执行栈为空时,主线程便会循环往复地读取任务队列中的事件,进入执行栈执行,这个过程叫 Event Loop。主线程对任务队列的读取也有先后之分,首先会去查找微任务,微任务队列的事件都执行完毕后,再读取最前面的宏任务进行执行,执行完再读取微任务队列,这个过程也是循环往复的。
一文告诉你 Event Loop 是什么?的更多相关文章
- 用大白话告诉你什么是Event Loop
文章原文地址 前沿 从前有座山,山里有座庙,庙里有个小和尚在讲故事.讲什么呢?讲的是: 从前有座山,山里有座庙,庙里有个小和尚在讲故事.讲什么呢?讲的是: 从前有座山,山里有座庙,庙里有个小和尚在讲故 ...
- 一文梳理JavaScript 事件循环(Event Loop)
事件循环(Event Loop),是每个JS开发者都会接触到的概念,但是刚接触时可能会存在各种疑惑. 众所周知,JS是单线程的,即同一时间只能运行一个任务.一般情况下这不会引发问题,但是如果我们有一个 ...
- [转载]JavaScript 运行机制详解:再谈Event Loop
https://app.yinxiang.com/shard/s8/sh/b72fe246-a89d-434b-85f0-a36420849b84/59bad790bdcf6b0a66b8b93d5e ...
- 【朴灵评注】JavaScript 运行机制详解:再谈Event Loop
PS: 我先旁观下大师们的讨论,得多看书了~ 别人说的:“看了一下不觉得评注对到哪里去,只有吹毛求疵之感. 比如同步异步介绍,本来就无大错:比如node图里面的OS operation,推敲一下就 ...
- [译]Node.js - Event Loop
介绍 在读这篇博客之前,我强列建议先阅读我的前两篇文章: Getting Started With Node.js Node.js - Modules 在这篇文章中,我们将学习 Node.js 中的事 ...
- JavaScript 运行机制详解:再谈Event Loop
原文地址:http://www.ruanyifeng.com/blog/2014/10/event-loop.html 一年前,我写了一篇<什么是 Event Loop?>,谈了我对Eve ...
- JavaScript 运行机制详解:深入理解Event Loop
Philip Roberts的演讲<Help, I'm stuck in an event-loop>,详细.完整.正确地描述JavaScript引擎的内部运行机制. 一.为什么JavaS ...
- 对Node.JS的事件轮询(Event Loop)的理解
title: Node.JS的事件轮询(event loop)的理解 categories: 理解 tags: Node JS 机制 当我们知道I/O操作和创建新线程的开销是巨大的! 网站延迟的开销 ...
- javascript运行机制详解: 再谈Event Loop(转)
作者: 阮一峰 日期: 2014年10月 8日 一年前,我写了一篇<什么是 Event Loop?>,谈了我对Event Loop的理解. 上个月,我偶然看到了Philip Roberts ...
随机推荐
- 五,session数据写入memcached
1,session数据通常保存在服务器端的文件中,它的默认过期时间是1440s.我们可以将session数据保存到memcached中,设定memcached的过期时间大于session过期时间即可. ...
- js面试题-数组去重
今天,在聊天群里看到数组去重的话题,面试者的答案如下: 参考答案如下: 程序员思维,做出如下测试: 未考虑到:1,‘1’是不同的,应该不去重 未考虑到对象 所以,参考答案只能去重基础类型 根据以往看过 ...
- 利用Django做一个简单的分页页面
views代码: from django.shortcuts import render from django.conf import settings from booktest.models i ...
- #阿里云#云服务器搭建git服务器
前言:大家都知道,git是非常方便的版本控制工具,目前网上有很多免费的git仓库可以给我们使用,但是有些时候我们并不放心将我们的项目寄放在别人的服务器上,这个时候就需要自己搭建一个git服务器,十分的 ...
- linux内核模块的安全
linux可以动态的加载内核模块,在很多场合可能需要确保加载内核的安全性.如果被攻击者加载恶意内核模块,将会使得内核变得极其危险. 当然,稳妥的做法就是给内核模块进行签名,内核只加载能正确验证的签名. ...
- sql server数据行号
select ROW_NUMBER() over(order by createTime desc) as RowNum,NoticeContent,CreateTime from PTS_Notic ...
- ms-sql 给表列添加注释
需求: 在创建数据库是对相应的数据库.表.字段给出注释. 解决方案: 首先,要明确一点的是注释存在sysproperties表中而不是跟创建的表捆绑到一起的(我的理解). 一.使用SQL Server ...
- js验证整数,浮点数
//验证数字(整数.浮点数都可以通过) function isfloat(oNum){ if(!oNum) return false; var strP=/^\d+(\.\d+)?$/; if(!st ...
- webpack4重新梳理一下2
上一篇已经实现了webpack的基本打包操作,但是并没有使用配置文件,而是使用 CLI 来实现打包. 配置文件 // webpack.config.js module.exports = { //入口 ...
- ActiveMQ消息持久化到Mysql数据库
1.把连接MySQL数据库的jar文件,放到ActiveMQ的lib目录下 2.修改ActiveMQ的conf目录下的activemq.xml文件,修改数据持久化的方式2.1 修改原来的kahadb的 ...