对javascript EventLoop事件循环机制不一样的理解
前置知识点:
- 浏览器原理,浏览器内核5种线程及协作,JS引擎单线程设计
推荐阅读: - js异步编程,Promise实现
推荐阅读: - 堆、栈、队列、执行栈、任务、微任务、事件循环机制♻️
推荐阅读:
事件循环机制
相信读者读完以上推荐的文章后,已经知道
事件循环机制
是怎么一回事了吧,也能从容应对面试。接下来我要谈谈自己的理解:
为什么会有事件循环机制
- js设计之初就是单线程模式,代码也都是顺序执行,当遇到因为大量计算、http请求等需要额外的等待时间时,浏览器用户就会体验到卡顿了,所以所有的设计和改进初衷只有一个就是要快。
事件循环机制的产生
浏览器说我的内核是多线程,可以辅助JS引擎线程啊,Web Worker线程提供大量计算辅助(不能操作DOM),事件触发线程,定时触发器线程, 异步http请求线程。
执行栈(先进后出),由JS引擎线程控制,引用下面这个例子谈谈自己的理解:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18console.log('script start'); setTimeout(function() {
console.log('setTimeout');
}, 0); Promise.resolve().then(function() {
console.log('promise1');
}).then(function() {
console.log('promise2');
});
console.log('script end'); // "script end"
// "promise1"
// "promise2"
// "setTimeout"在
ES5
还没有Promise
时代,异步回调很常见,上面例子中,通过解读Promise源码(前端面试必考题Promise的源码解析),我们可以把Promise
转换成如下图式回调(个人理解,文章中的Promise源码也只是模拟,大部分浏览器已经原生支持)。打印完
script start
,script end
主执行栈出栈,如果Promise.resolve().then
换成new Promise(executor)
,脑补Promise
换成回调函数,那么这个函数一执行,executor
函数也就执行了,然后遇到异步回调,回调函数被其它对应的线程接手,启动观察者模式,完成后回调函数被推入事件任务队列,等待执行栈空了进入主线程执行以上这种在异步函数中放同步函数的例子,为了合理解释输出顺序而推出了
microtasks
微任务的概念,请看下面的例子,脑补Promise
换成回调函数,Promise.prototype.then
内部执行了return new Promise()
,js引擎在捕捉到Promise
时,放到了由js引擎自身控制的微任务队列等待执行,也就造成promise1、2、3、4错开打印1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24console.log('script start'); new Promise(function(resolve, reject) {
console.log('promise1');
resolve();
}).then(function() {
console.log('promise2');
}); new Promise(function(resolve, reject) {
console.log('promise3');
resolve();
}).then(function() {
console.log('promise4');
}); console.log('script end'); // "promise1"
// "promise3"
// "script end"
// "promise2"
// "promise4"micro 大专栏 对javascript EventLoop事件循环机制不一样的理解tasks
微任务的概念完全为了解释异步函数中放同步函数的场景,而且各类文章和面试都是这种题目和例子,在实际开发过程中,你会在Promise
中这么写么?,在我看来这种比较打印顺序太过于理论,而且可能会混乱你的思绪。就像下面的例子,Promise
的resolve
决定了Promise状态,就像在回调函数中满足了条件才会继续执行,例子中只是用setTimeout
模拟异步请求,用之前的理论你可能觉得setTimeout
被放入了事件任务队列,那没有resolve
的Promise
怎么解释呢?(放到微任务里一直阻碍第一个setTimeout
宏任务执行吗?显然是不可能的,这不是跟设计事件循环机制初衷冲突了么)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22setTimeout(function() {
console.log('setTimeout');
}, 0); new Promise(function(resolve, reject) {
setTimeout(function() { // 模拟异步请求
console.log('promise1');
resolve();
}, 0);
}).then(function() {
console.log('promise2');
}); new Promise(function(resolve, reject) {
// resolve(); 注释掉resolve,使Promise一直处于‘pending’状态
}).then(function() {
console.log('promise2');
}); // "setTimeout"
// "promise1"
// "promise2"个人认为把
Promise
,async/await
脑补成原始的回调函数(模拟源码中模拟异步是用的setTimeout
函数),而js引擎捕捉到setTimeout
,setInterval
就转给定时触发器线程处理,捕捉到XMLHttpReuqest
,fetch
就转给异步http请求线程,跟事件触发线程一起管理着事件任务队列,微任务的概念可以看作是当事件触发线程遇到几乎同时需要把回调函数放到事件任务队列时,Promise
内部的异步标识函数优先级高于setTimeout
函数吧,以上例子中没有执行resolve
的Promise
状态一直处于’pending’,事件触发线程压根没有放入到事件任务队列,总之浏览器会安排的妥妥的,不要打架,虽然js引擎线程只有一个(听我指挥排好队,咱们这都是同步代码执行ms级别,我开了很多其它线程处理需要等待的代码了)。以下例子模拟所谓的几乎同时把回调函数放到事件任务队列,记得把Promise
脑补成原始的回调函数。仿佛回到了没有微任务的时代。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27setTimeout(function() {
console.log('setTimeout');
}, 0); new Promise(function(resolve, reject) {
setTimeout(function() { // 模拟异步请求
console.log('promise1');
resolve();
}, 2000);
}).then(function() {
console.log('promise2');
}); new Promise(function(resolve, reject) {
setTimeout(function() { // 模拟异步请求
console.log('promise3');
resolve();
}, 2000);
}).then(function() {
console.log('promise4');
}); // "setTimeout"
// "promise1"
// "promise2"
// "promise3"
// "promise4"
以上内容纯属未深入了解js情况下的个人理解,感觉是在努力摒弃微任务的概念,回归ES5回调函数时代,便于自身理解事件循环机制而做出的遐想。
多环境下的事件循环机制
在node环境、浏览器环境以及各个不同版本下js引擎处理的方式还不太一样。node比浏览器还复杂些
对javascript EventLoop事件循环机制不一样的理解的更多相关文章
- JavaScript的事件循环机制浅析
前言 JavaScript是一门单线程的弱类型语言,但是我们在开发中,经常会遇到一些需要异步或者等待的处理操作. 类似ajax,亦或者ES6中新增的promise操作用于处理一些回调函数等. 概念 在 ...
- 【运行机制】 JavaScript的事件循环机制总结 eventLoop
0.从个例子开始 //code-01 console.log(1) setTimeout(() => { console.log(2); }); console.log(3); 稍微有点前端经验 ...
- javascript的事件循环机制
JavaScript是一门编程语言,既然是编程语言那么就会有执行时的逻辑先后顺序,那么对于JavaScript来说这额顺序是怎样的呢? 首先我们我们需要明确一点,JavaScript是单线程语言.所谓 ...
- JavaScript 运行机制:Event事件循环机制
JavaScript Event事件循环机制 JS是单线程的,浏览器只分配一个主线程给JS.一次只能执行一个任务,当前任务执行完后在可以执行下一个任务.任务多时,就会形成任务队列排队等待执行.但是非常 ...
- 深入理解JavaScript事件循环机制
前言 众所周知,JavaScript 是一门单线程语言,虽然在 html5 中提出了 Web-Worker ,但这并未改变 JavaScript 是单线程这一核心.可看HTML规范中的这段话: To ...
- javascript事件循环机制 浅尝手记
引入 众所周知Javascript是一个单线程的机制,虽然可以依托多线程的浏览器实现页面如何实现页面复杂的渲染.事件响应,但仍不会改变其单线程的本质:所以对于js的事件循环机制的了解是一个前端人员的必 ...
- selector.select(500); EventLoop及事件循环机制 netty 在半透明做代理网关下 对请求的批处理
Netty框架学习之路(五)—— EventLoop及事件循环机制 - 懋懋之为 - CSDN博客 https://blog.csdn.net/tjreal/article/details/79751 ...
- 深入浅出Javascript事件循环机制
一.JS单线程.异步.同步概念 众所周知,JS是单线程(如果一个线程删DOM,一个线程增DOM,浏览器傻逼了-所以只能单着了),虽然有webworker酱紫的多线程出现,但也是在主线程的控制下.web ...
- 浏览器中的JavaScript事件循环机制
浏览器的事件循环机制是HTML中定义的规范. JavaScript有一个主线程和调用栈,所有的任务都会被放到调用栈等待主线程执行. JS调用栈 是一种先进后出的数据结构.当函数被调用时,会被添加到栈中 ...
随机推荐
- jacascript 滚动scroll
滚动 scroll scrollHeight 表示元素的总高度,包括由于溢出而无法展示在网页的不可见部分: scrollWidth 表示元素的总宽度,包括由于溢出而无法展示在网页的不可见部分: 没有滚 ...
- gcc -E xx.c
C语言代码在交给编译器之前,会先由预处理器进行一些文本替换方面的操作,例如宏展开.文件包含.删除部分代码等. 在正常的情况下,GCC 不会保留预处理阶段的输出文件,也即.i文件.然而,可以利用-E选项 ...
- ESLint javascript格式要求
首行缩进2个空格 eslint: indent functionhello (name) { console.log('hi', name) } 字符串使用单引号(除了避免转义) eslint: qu ...
- CodeForces 1005D Polycarp and Div 3(思维、贪心、dp)
http://codeforces.com/problemset/problem/1005/D 题意: 给一个仅包含数字的字符串,将字符串分割成多个片段(无前导0),求这些片段里最多有多少是3的倍数 ...
- Java之异常的处理(throws)
import java.io.File;import java.io.FileInputStream;import java.io.FileNotFoundException;import java. ...
- ubuntu16.04更换 apt-get 阿里源
deb-src http://archive.ubuntu.com/ubuntu xenial main restricted #Added by software-properties deb ht ...
- linux下用firefox打开csdn故障解决办法
问题:浏览器打开csdn时博客浏览不全,没有了排版 解决办法: 1.下载一个安装包 命令:epel-release-7-11.noarch.rpm: epel-release-7-11.noarch ...
- learning Perl:91行有啥用? 88 print "\n----------------------------------_matching_multiple-line_text--------------------------\n"; 91 my $lines = join '', <FILE>;
89 open FILE, "< file_4_ex_ch7.txt" 90 or die "cannot open file: $!"; ...
- Sqlite教程(2) Data Access Object
因为这个项目的业务层很薄,因此想在架构上尽量保持着「轻」,不会把创建DbHelper的interface. 而是直接用DAO创建DbHelper对象. DAO和DbHelper也是同样使用懒汉模式. ...
- linux_cat命令
cat 命令可以用来显示文本文件的内容(类似于 DOS 下的 type 命令),也可以把几个文件内容附加到另一个文件中,即连接合并文件. 关于此命令,有人认为写 cat 命令的人是因为喜欢猫,因此给此 ...