浏览器中JS单线程机制与异步的实现_hehaonan~_浏览器单线程
本文主要讨论浏览器环境上的js单线程机制和js异步的实现,关于其他环境下,比如node就暂时不讨论
?
先来说结论:
- JS本身是没有办法真正实现异步的,异步的实现需要环境的支持(比如浏览器,因为浏览器是多线程的)。JS有两个任务队列,分别是宏任务(macrotasks)队列和微任务(microtask)队列,JS也只有一个任务执行栈,执行栈只能放一个宏任务或一个微任务,执行完这个任务后清空执行栈。浏览器有一个event table,用来确定宏任务或者微任务何时被添加到相应队列的队尾(个人觉得这个才是JS异步的核心)。当用户打开一个页面的时候,JS的执行栈有一个宏任务,也就是一整个<script>代码,JS就会执行这个宏任务,当遇到宏任务时候,就会把这个宏任务注册到event table,JS继续执行下面的(同步)代码,这个宏任务什么时候被添加到宏任务队尾是由浏览器来控制,同样的遇到微任务,也会有类似的操作,当<script>的(同步)代码执行完后就会来一个个的处理微任务队列里的微任务,当微任务队列里的微任务执行完了,就会从宏任务队列取出一个宏任务,重复进行上述操作,这个过程叫Event Loop(事件循环)。粗略的讲,就是“一个宏任务———几个微任务”,这样子不断的循环。
图片来自:Event loop: microtasks and macrotasks (javascript.info)?
这里rander就把他当作一类普通的宏任务就可以了,执行的循序取决于渲染事件在宏任务队列里的顺序,并不一定是像图中一样的这么规律。?
哪些是macrotask?
- setTimeOut/setInterval函数I/O操作(addEventListener的回调等)?<script>代码界面的渲染?.……欢迎评论区补充
哪些是microtask?
- Promise的then、catch、finally、all、race??await(本质也是Promise)?queueMicrotask? MutationObserver? ……欢迎评论区补充
从setTimeOut来看macrotask
先上题目:
console.log('1')
setTimeout(()=>{
?? ?console.log('2');
},0);
console.log('3');
????????显然,这个的输出结果是1-3-2。
????????为什么不是1-2-3呢?setTimeout函数的时间设置不是0秒么?
????????前面说了setTimeOut是一个macrotask,这里的时间参数是0s(这里的0秒指的是从event table被压入宏任务队列的时间,不是真的过了0秒就马上执行),setTimeOut的回调函数在event table停了“0s”后马上被排到宏任务队列的队尾,但是这个宏任务要等执行栈里的宏任务执行完后再放入执行栈执行。
????????当前的宏任务执行完后,也就是输出1和2后,从宏任务队列里面拿出一个宏任务(setTimeOut的回调函数),放入执行栈并执行,然后输出3。
一个宏任务何时被放入event table取决于它什么时候被执行到(这里的“执行到”并不是指的宏任务被整个执行了,单纯的指V8引擎,碰到宏任务的标志,比如setTimeOut),一个宏任务什么时候被排到宏任务的队尾就要看触发条件(各种事件、定时器)什么时候触发,这里的setTimeOut就是什么时候时间到了,就被排到队尾。
????????这里的计时操作并不是JS的线程来干的,而是浏览器。
从promise来看microtask
这里不会promise的可以先去补一下,要不然有点吃力?
console.log('script start');
new Promise(function(resolve) {
? ? console.log('promise1');
? ? resolve();
}).then(function() {
? ? console.log('promise2');
});
console.log('script end');
输出结果:script start-->promise1-->script end-->promise2
!!!这里要注意,promise的里面的执行函数是同步的,异步的是then的回调。
来看看执行的过程:
- ??先执行console.log('script start'),输出script start。??执行new Promise,并把后面then里的回调函数(微任务)放入event table。??执行new Promise 里的执行函数,执行到 console.log('promise1'),输出promise1,执行到resolve(),就把对应的微任务从event table排到微任务队列的队尾。??执行console.log('script end'),输出script end。??这里是一个分界线,一个执行栈里的一个宏任务执行完了,从微任务队列里一个个取出微任务放到执行栈执行,也就是把前面then的回调取出来,并执行,输出promise2。
复杂的情况下
function fn(){
console.log(1);
setTimeout(() => {
console.log(2);
Promise.resolve().then(() => {
console.log(3)
});
});
new Promise((resolve, reject) => {
console.log(4)
resolve(5)
}).then((data) => {
console.log(data);
Promise.resolve().then(() => {
console.log(6)
}).then(() => {
console.log(7)
setTimeout(() => {
console.log(8)
}, 0);
});
})
setTimeout(() => {
console.log(9);
})
console.log(10);
}
fn();
输出的结果:1,4,10,5,6,7,2,3,9,8
执行过程:?
- ?首先一开始<script>这个宏任务存在于执行栈里被执行,执行fn函数,执行到console.log(1),输出1。
- 执行栈:栈顶【(1-33)】栈底宏任务队列:队头【】队尾微任务队列:对头【】队尾
参考连接
前端干货:JS的执行顺序 - 简书 (jianshu.com)
Event loop: microtasks and macrotasks (javascript.info)
Microtasks (javascript.info)
相关文章
- Netty进阶实现自定义Rpc_兜兜转转m
- Kafka第三章:新旧节点更替_超哥--
- 【CS224图机器学习】task4 图嵌入表示学习_王多头发
- 运筹系列65:TSP问题的精确求解法概述_IE06
- RFC4543: Galois Message Authentication Code (GMAC);CONFIG_CRYPTO_GCM_mzhan017
- Jmeter常用断言之BeanShell断言详解_沫沫18S
- ros中时间的概念:ros::Time、ros::Duration、定时器ros::Timer&ros::Rate、ros::WallTime_小蜗牛冲鸭~
- 【自监督论文阅读笔记】MVP: Multimodality-guided Visual Pre-training_YoooooL_
- actipro-winforms-controls-23.1.0 Crack_john_dwh
- 中国高速公路行业市场规模及未来发展趋势_a18811791343
- V4L2操作流程_<( ̄? ̄)小小程序员
- Git复习_读书健身敲代码
- 微服务03 分布式搜索引擎 elasticsearch ELK kibana RestAPI 索引库 DSL查询 RestClient 黑马旅游_软工菜鸡
- 力扣5-最长回文子串_Wu_ShF
- C#中多态、抽象类、虚方法_花开莫与流年错_
- python基于vue学生毕业离校系统_QQ1963288475