详解队列在前端的应用,深剖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 是先执行同步代码,所以先打印 HiBye ,之后执行异步代码,打印出 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 是单线程的;
  • 异步( setTimeoutajax 等)使用回调,基于 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*/

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

2、宏任务和微任务

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

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

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

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

(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 机制。所以,在上面的这段代码中,程序会先打印同步代码 HiBye ,等待同步代码打印完毕后,会再查找 DOM 事件,进行渲染,最后再触发 event loop

总结 event loopDOM 渲染的关系:

  • 在程序执行的时候, 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 渲染,便于查看效果)
});

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

微任务

在图中可以看出,微任务 promiseDOM 渲染前就触发了,所以 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 的关系。在第一个演示中,微任务 PromiseDOM 还没有渲染时就触发了,所以微任务都是在 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.mzph.cn/news/307975.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈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值存进去前两个数的和并记录其个数,然后在后面两个数…

ES6的Set和Map你都知道吗?一文了解集合和字典在前端中的应用

一文了解集合和字典在前端中的应用一、&#x1f4dd;集合1、集合是什么&#xff1f;2、前端与集合&#xff1a;使用ES6中的Set3、用Set模拟并集、交集和差集&#xff08;1&#xff09;模拟并集运算&#xff08;2&#xff09;模拟交集运算&#xff08;3&#xff09;模拟差集运算…