详解队列在前端的应用,深剖JS中的事件循环Eventloop,再了解微任务和宏任务

队列在前端中的应用

队列在前端中的应用

  • 一、队列是什么
  • 二、应用场景
  • 三、前端与队列:事件循环与任务队列
    • 1、event loop
    • 2、JS如何执行
    • 3、event loop过程
    • 4、 DOM 事件和 event loop
    • 5、event loop 总结
  • 四、宏任务和微任务
    • 1、引例
    • 2、宏任务和微任务
      • (1)常用的宏任务和微任务
      • (2)宏任务和微任务的优先级
      • (3)代码实现微任务和宏任务
      • (4)event loop和DOM渲染
      • (5)微任务、宏任务和DOM渲染的关系
      • (6)为何微任务更早
  • 五、结束语

队列 在日常生活中的应用非常广泛,比如我们最熟悉不过的食堂排队打饭、击鼓传花等等问题。同时,它在前端中的应用也非常广泛,比如,事件循环 Event loop 、JS异步中的任务队列。

所以呢,对于前端来说, 队列 结构是一个必学的知识点。在接下来的这篇文章中,将讲解关于 队列 在前端中的应用。

一、队列是什么

队列是一种先进先出(FIFO)的线性表。它只允许在表的一端进行插入,而在另一端删除元素。

二、应用场景

  • 需要先进先出的场景。
  • 比如:食堂排队打饭、火车站排队买票、JS异步中的任务队列、计算最近请求次数……。

三、前端与队列:事件循环与任务队列

1、event loop

event loop,也被称为事件循环或事件轮询。因为JS是单线程运行的,且异步需要基于回调来实现,所以, event loop 就是异步回调的实现原理。

2、JS如何执行

JS在程序中的执行遵循以下规则:

  • 从前到后,一行一行执行
  • 如果某一行执行报错,则停止下面代码的执行
  • 先把同步代码执行完,再执行异步

一起来看一个实例:

console.log('Hi');setTimeout(function cb1(){console.log('cb1'); //cb1 即callback回调函数
}, 5000);console.log('Bye');//打印顺序:
//Hi
//Bye
//cb1

从上例代码中可以看到, JS 是先执行同步代码,所以先打印 Hi 和 Bye ,之后执行异步代码,打印出 cb1 。

以此代码为例,下面开始讲解 event loop 的过程。

3、event loop过程

对于上面这段代码,执行过程如下图所示。

在这里插入图片描述

从上图中可以分析出这段代码的运行轨迹。首先 console.log('Hi') 是同步代码,直接执行并打印出 Hi 。接下来继续执行定时器 setTimeout ,定时器是异步代码,所以这个时候浏览器会将它交给 Web APIs 来处理这件事情,因此先把它放到 Web APIs 中,之后继续执行 console.log('Bye') , console.log('Bye') 是同步代码,在调用堆栈 Call Stack 中执行,打印出 Bye 。

到这里,调用堆栈 Call Stack 里面的内容全部执行完毕,当调用堆栈的内容为空时,浏览器就会开始去任务队列寻找下一个任务,此时任务队列就会去 Web API 里面寻找任务,遵循先进先出原则,找到了定时器,且定时器里面是回调函数 cb1 ,于是把回调函数 cb1 传入任务队列中,此时 Web API 也空了,任务队列里面的任务就会传入到调用堆栈里Call Stack 里执行,最终打印出 cb1 。

4、 DOM 事件和 event loop

先来看两段代码。

console.log('Hi');setTimeout(function cb1(){console.log('cb1'); //cb 即 callback
}, 5000);console.log('Bye');/*输出结果:
Hi
Bye
cb1
*/
<button id = "btn1">提交</button><script>   console.log('Hi');document.getElementById('btn1').click(function(e){console.log('button clicked');});console.log('Bye');
</script>/*输出结果:
Hi
Bye
button clicked
*/

以上这两段代码中,第一段是关于 setTimeout 的事件循环,第二段是关于 DOM 事件的事件循环。那有小伙伴就会有疑问说, DOM 事件不是异步操作吗,为什么输出结果依然是在最后呢?

其实, DOM 事件确实不是异步操作,但是它也使用回调,基于 event loop 事件循环机制,所以当我们点击的时候,会触发 DOM 事件,并进行打印。

总结下 DOM 事件和 event loop 的区别:

  • JS 是单线程的;
  • 异步( setTimeout , ajax 等)使用回调,基于 event loop ;
  • DOM 事件不是异步,但也使用回调,基于 event loop 。

5、event loop 总结

初阶认识完event loop后,来做个总结:

总结event loop 过程1

  • 同步代码,一行一行放在 Call Stack 执行;
  • 遇到异步,会先“记录”下,等待时机(定时、网络请求);
  • 时机到了,就移动到 Callback Queue。

总结event loop 过程2

  • 如果 Call Stack 为空(即同步代码执行完),则 event Loop 开始工作;
  • 轮询查找 Callback Queue ,如果有则移动到 Call Stack 执行;
  • 然后继续轮询查找(跟永动机一样,不断循环查找)。

四、宏任务和微任务

1、引例

我们先来看一段代码。

console.log(100);
setTimeout(() => {console.log(200);
});
Promise.resolve().then(() => {console.log(300);
});
console.log(400);
/*** 打印结果:* 100* 400* 300* 200*/

在上面这段代码中,第一个和第二个打印结果是基于同步,我们都知道要打印 100 和 400 ,但是第三个和第四个打印结果,理论上按照打印顺序应该是 200 和 300 才是,为什么是打印 300 和 200 呢?这就涉及到一个宏任务和微任务的问题。接下来将对宏任务和微任务进行讲解。

2、宏任务和微任务

(1)常用的宏任务和微任务

名称举例(常用)
宏任务script、setTimeout 、setInterval 、setImmediate、Ajax、DOM事件、I/O、UI Rendering
微任务process.nextTick()、Promise、async/await

上述的 setTimeout 和 setInterval 等都是任务源,真正进入任务队列的是他们分发的任务。

注意: 微任务执行时机比宏任务要早!!

(2)宏任务和微任务的优先级

优先级

  • setTimeout = setInterval 一个队列
  • setTimeout > setImmediate
  • process.nextTick > Promise

(3)代码实现微任务和宏任务

for(const macroTask of macroTaskQueue){handleMacroTask();for(const microTask of microTaskQueue){handleMicroTask();}
}

(4)event loop和DOM渲染

在上面的主题三第4点中讲过, DOM 事件基于回调,也是基于 event loop 机制的。那DOM事件在程序执行到什么时候,才会渲染呢?

同样来看这段代码。

<button id = "btn1">提交</button><script>   console.log('Hi');document.getElementById('btn1').click(function(e){console.log('button clicked');});console.log('Bye');
</script>/*输出结果:
Hi
Bye
button clicked
*/

event loop 和 DOM渲染

由上图可知,当程序调用栈空闲时,程序会先尝试去进行 DOM 渲染,最后再触发 Event Loop 机制。所以,在上面的这段代码中,程序会先打印同步代码 Hi 和 Bye ,等待同步代码打印完毕后,会再查找 DOM 事件,进行渲染,最后再触发 event loop 。

总结 event loop 和 DOM 渲染的关系:

  • 在程序执行的时候, JS 是单线程的,且和 DOM 渲染共用一个线程;

  • 所以 JS 在执行的时候,得留一些时机提供给 DOM 渲染。

  • 每次 Call Stack 清空(即每次轮询结束),表示同步任务执行完成;

  • 程序会一直给 DOM 重新渲染的机会, DOM 结构如有改变则重新渲染;

  • 然后再去触发下一次 Event Loop 。

(5)微任务、宏任务和DOM渲染的关系

先了解微任务、宏任务和 DOM 渲染的关系:

  • 宏任务: DOM 渲染后触发,如 setTimeout 。
  • 微任务: DOM 渲染前触发,如 Promise 。

我们先来演示现象,再追究其原理。

1)演示1

const $p1 = $('<p>一段文字</p>');
const $p2 = $('<p>一段文字</p>');
const $p3 = $('<p>一段文字</p>');
$('#container').append($p1).append($p2).append($p3);//微任务:DOM 渲染前触发
Promise.resolve().then(() => {console.log('length', $('#container').children().length);alert('Promise then');//(alert 会阻断 js 执行, 也会阻断 DOM 渲染,便于查看效果)
});

以上这段代码中,浏览器显示效果如下。

微任务

在图中可以看出,微任务 promise 在 DOM 渲染前就触发了,所以 DOM 对应的文字还没显示时, Promise 就已经打印。

2)演示2

const $p1 = $('<p>一段文字</p>');
const $p2 = $('<p>一段文字</p>');
const $p3 = $('<p>一段文字</p>');
$('#container').append($p1).append($p2).append($p3);//宏任务:DOM 渲染后触发
setTimeout(() => {console.log('length1', $('#container').children().length);alert('SetTimeout');//(alert 会阻断 js 执行, 也会阻断 DOM 渲染,便于查看效果)
});

以上这段代码中,浏览器显示效果如下。

宏任务

在图中可以看出,当 DOM 对应的文字已经显示时, setTimeout 弹框才出现,所以宏任务 setTimeout 是在 DOM 渲染后(即 DOM 渲染并显示结束)才触发。

讲到这里,回到我们前面所说的知识点。

  • 宏任务: DOM 渲染后触发,如 setTimeout 。
  • 微任务: DOM 渲染前触发,如 Promise 。

从上面的演示后,相信大家应该明白了微任务、宏任务和 DOM 的关系。在第一个演示中,微任务 Promise 在 DOM 还没有渲染时就触发了,所以微任务都是在 DOM 渲染前触发。在第二个演示中,宏任务 setTimeout 在文字显示结束后才触发 alert ,所以微任务都是在 DOM 渲染后才进行触发。

(6)为何微任务更早

理解完微任务和宏任务与DOM的关系后,我们也大致基本了解了为什么微任务比宏任务更早。接下来我们在从 eventloop 层面来看,为什么微任务会比宏任务更早,为什么会在DOM渲染前就开始触发呢?

先用一张图来表示。

微任务宏任务

微任务在执行时不会经过 Web APIs ,它会把它放到一个叫做 micro task queue(即宏任务队列)当中。且微任务是ES6` 语法规定的,宏任务是由浏览器规定的,所以它会比宏任务更早。

到这里,我们讲完了 event loop 以及与其相关的宏任务和微任务,下面我们再用一张图来总结实际运用的执行顺序。

微任务宏任务

从上图中可以得出结论:

第一步,程序先程序 Call Stack 里面的内容,待 Call Stack 清空时,执行当前的微任务;

第二步,程序找到微任务队列的任务,执行微任务;

第三步,待微任务执行完毕后,尝试执行DOM渲染;

第四步, DOM 渲染结束后,触发 event loop ,执行宏任务。

五、结束语

队列在前端中的应用可以算是很非常频繁了。基本上我们写的异步函数在执行过程中,都会涉及到事件循环问题。且在前端的面试当中,经常会被问到 event loop 、事件循环或者事件轮询是什么,很多面试者就很容易在这块内容吃亏。相信通过上文的学习,大家都对 eventloop 、微任务和宏任务有了一个更深的认识。

队列在前端中的应用就讲到这里啦!如有不理解或者文章有误欢迎评论区留言或私信我交流~

  • 关注公众号 星期一研究室 ,第一时间关注学习干货,更多有趣的专栏待你解锁~

  • 如果这篇文章对你有用,记得点个赞加个关注再走哦~

  • 我们下期见!🥂🥂🥂

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.tpcf.cn/news/307975.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

终于弄明白了 Singleton,Transient,Scoped 的作用域是如何实现的

一&#xff1a;背景1. 讲故事前几天有位朋友让我有时间分析一下 aspnetcore 中为什么向 ServiceCollection 中注入的 Class 可以做到 Singleton&#xff0c;Transient&#xff0c;Scoped&#xff0c;挺有意思&#xff0c;这篇就来聊一聊这一话题&#xff0c;自从 core 中有了 S…

leetcode142. 环形链表 II(暴力+双链表)

一:题目 二:思路 1.双指针 快慢指针(快指针一次一个结点&#xff0c;慢指针一次两个结点) 2.如果有环的话&#xff0c;那么快慢指针肯定会相遇 3.那么相遇的地点一定在环中 因为如果没有环的话慢指针是永远追不到快指针的 4.接下来就是判断出口在那里&#xff0c;我们定义一个…

动态 Restful API 生成

介绍通常在DDD开发架构中&#xff0c;我们写完服务层需要在控制器中写API&#xff0c;今天介绍一个组件 Plus.AutoApi 可以用它来动态生成 Restful 风格的 WebApi&#xff0c;不用写 Controller。快速使用在你的应用服务层中添加组件Install-Package Plus.AutoApi在 Startup 中…

卷死了!再不学vue3就没有人要你了!速来围观vue3新特性

一文全面了解vue3新特性一、&#x1f636;vue3比vue2有什么优势&#xff1f;二、&#x1f9d0;Vue3升级了哪些重要的功能1、createApp2、emits(父子组件间的通信)&#xff08;1&#xff09;通信方式&#xff08;2&#xff09;举个例子&#x1f330;3、多事件处理4、Fragment5、…

idea报错Class not found (在target中没有生成对应的class文件)

一&#xff1a;问题描述 二:解决 既然他不自动生成&#xff0c;那么我们就手动导入&#xff1b; 点击后应用 然后再次运行我们的测试用例&#xff1b;如果不行 再取消勾选 然后再运行我们的测试用例

敲黑板!vue3重点!一文了解Composition API新特性:ref、toRef、toRefs

一文了解Composition API新特性&#xff1a;ref、toRef、toRefs一、&#x1f64e;如何理解ref、toRef和toRefs1、ref、toRef和toRefs是什么&#xff08;1&#xff09;ref1&#xff09;ref是什么2&#xff09;举个例子&#x1f330;&#xff08;2&#xff09;toRef是什么1&#…

C# 枚举转列表

C# 枚举转列表独立观察员 2020 年 9 月 1 日今天有朋友问我&#xff0c;ComboBox 怎么绑定一个 Enum&#xff0c;其实他的意思是枚举如何转换为列表。想想这确实是一个挺正常的需求&#xff0c;但我一时也只想到遍历&#xff0c;他觉得麻烦&#xff0c;于是我在网上帮忙查了一下…

leetcode242. 有效的字母异位词(两种方法map或数组)

一:题目 二:上码 1:方法一&#xff08;map解法&#xff09; class Solution { public:bool isAnagram(string s, string t) {/**思路:1.分析题意&#xff0c;这个是要判断t中的字符出现次数和s中字符出现的次数相同2.可以用map<char,int>来做*/map<char,int>m1,m…

活久见!月薪30k的小程序全栈开发到底有多难?

10年前&#xff0c;公司的标配是门户网站&#xff0c;造就了一批网站工作室。随着移动互联网大潮兴起&#xff0c;App又成了企业标配&#xff0c;IOS和Android开发赚的盆满钵满。然而App导致的手机内存告急&#xff0c;无止尽的信息推送&#xff0c;让微信小程序应运而生。然而…

卷不动也得继续学!紧跟vue3的步伐,再来get一波进阶新特性!

vue3进阶新特性一、&#x1f4d7;watch和watchEffect1、watch和watchEffect的区别2、举个例子&#xff08;1&#xff09;wtach监听&#xff08;2&#xff09;watchEffect监听二、&#x1f4d8;setup如何获取组件实例&#xff08;1&#xff09;为什么需要获取组件实例&#xff0…

leetcode349. 两个数组的交集(思路+详解)

一:题目 二:上码 class Solution { public:vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {/**思路:本题我们采用的哈希表数据结构是unordered_set,没有用数组因为我们不确定给出的数组中数值的范围如果 强上会出现空间的大量…

高效掌握新技能的「树型思维」

大家好&#xff0c;我是Z哥。不知道你有没有过这样的困惑&#xff0c;想学习某项新技能&#xff0c;但是很容失败。比如&#xff0c;出于职业发展的考虑&#xff0c;想学习一门新的编程语言&#xff0c;或者想了解一个新的技术框架&#xff1b;又或者看了某些综艺节目后想玩一玩…

vue2的响应式原理学“废”了吗?继续观摩vue3响应式原理Proxy

一文了解Vue3的响应式原理一、&#x1f7e9;回顾Object.defineProperty二、&#x1f7e8;Proxy基本使用三、&#x1f7e6;学习Proxy语法&#xff1a;Reflect四、&#x1f7e7;Vue3如何用Proxy实现响应式1、实现响应式2、Proxy总结&#xff08;1&#xff09;深度监听&#xff0c…

一名“企业定制化人才”的自诉:“我不愿意,但却无可奈何”

这是头哥侃码的第214篇原创几个月前&#xff0c;我曾在大智慧时期的一位同事跟我聊微信&#xff0c;说自己所在的公司这两年业务一直不温不火&#xff0c;而且从19年底就逐渐缩减技术人员。今年上半年又因为疫情的关系&#xff0c;公司干脆直接砍掉了剩余的三个技术部门&#x…

一文了解分而治之和动态规则算法在前端中的应用

一文了解分而治之和动态规则算法一、分而治之1、分而治之是什么&#xff1f;2、应用场景3、场景剖析&#xff1a;归并排序和快速排序二、动态规则1、动态规则是什么&#xff1f;2、应用场景3、场景剖析&#xff1a;斐波那契数列4、动态规则VS分而治之三、分而治之算法常见应用1…

leetcode1. 两数之和(两种方法)

一:题目 二:上码 1:方法一 class Solution { public:vector<int> twoSum(vector<int>& nums, int target) {vector<int> v;for(int i 0; i < nums.size() - 1; i) {for(int j i1; j < nums.size(); j) {if(nums[i] nums[j] target) {v.push_…

排坑 | Exceptionless 5.x 无法正常发送邮件

【问题解决】| 作者 / Edison Zhou这是恰童鞋骚年的第282篇原创内容你有碰到过通过docker部署Exceptionless无法发送邮件的问题吗&#xff1f;此解决办法适用于Exceptionless 5.x版本&#xff08;如果你不想升级6.x的话&#xff09;。1问题起因去年这个时候&#xff0c;得知Exc…

一文了解贪心算法和回溯算法在前端中的应用

一文了解贪心算法和回溯算法在前端中的应用一、贪心算法1、贪心算法是什么&#xff1f;2、应用场景3、场景剖析&#xff1a;零钱兑换二、回溯算法1、回溯算法是什么&#xff1f;2、什么问题适合选用回溯算法解决&#xff1f;2、应用场景3、场景剖析&#xff1a;全排列三、贪心算…

leetcode454. 四数相加 II(思路+详解)

一:题目 二:上码 class Solution { public:int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {/**思路:1.我们用map容器的key值存进去前两个数的和并记录其个数,然后在后面两个数…

[C#.NET 拾遗补漏]08:强大的LINQ

阅读本文大概需要 13 分钟。大家好&#xff0c;这是 [C#.NET 拾遗补漏] 系列的第 08 篇文章&#xff0c;今天讲 C# 强大的 LINQ 查询。LINQ 是我最喜欢的 C# 语言特性之一。LINQ 是 Language INtegrated Query 单词的首字母缩写&#xff0c;翻译过来是语言集成查询。它为查询跨…