-
不空 --> 执行最早进入队列的任务 --> 执行下一步
-
空 --> 执行下一步
- 判断微任务队列是否为空
-
不空 --> 执行最早进入队列的任务 --> 继续检查微任务队列空不空
-
空 --> 执行下一步
因为首次执行宏队列中会有 script(整体代码块)任务,所以实际上就是 Js 解析完成后,在异步任务中,会先执行完所有的微任务,这里也是很多
面试题喜欢考察的。
需要注意的是,新创建的微任务会立即进入微任务队列排队执行,不需要等待下一次轮回。
主线程从任务队列读取事件,这个过程是循环不断的,所以整个运行机制又称为 Event Loop(事件循环)。
在深入事件循环机制之前,需要弄懂一下几个概念:
-
执行上下文( Execution context )
-
执行栈( Execution stack )
-
微任务( micro-task )
-
宏任务( macro-task )
执行上下文( Execution context )
执行上下文是一个抽象的概念,可以理解为是代码执行的一个环境。JS 的执行上下文分为三种,
全局执 行上下文、函数(局部)执行上下文、Eval 执行上下文。
-
全局执行上下文
:全局执行上下文指的是全局 this 指向的 window ,可以是外部加载的 JS 文件 或者本地 标签中的代码。 -
函数执行上下文
:函数上下文也称为局部上下文,每个函数被调用的时候,都会创建一个新的局部 上下文。 -
Eval 执行上下文: 这个不常用,这里不说。
执行栈( Execution stack )
执行栈,就是我们数据结构中的“栈”,它具有“先进后出”的特点,正是因为这种特点,在我们代码进行 执行的时候,遇到一个执行上下文就将其依次压入执行栈中。
当代码执行的时候,先执行位于栈顶的执行上下文中的代码,当栈顶的执行上下文代码执行完毕就会出 栈,继续执行下一个位于栈顶的执行上下文。
function foo() {
console.log(‘a’);
bar();
console.log(‘b’);
}
function bar() {
console.log(‘c’);
}
foo();
-
初始化状态,执行栈任务为空。
-
foo 函数执行, foo 进入执行栈,输出 a ,碰到函数 bar 。
-
然后 bar 再进入执行栈,开始执行 bar 函数,输出 c 。
-
bar 函数执行完出栈,继续执行执行栈顶端的函数 foo ,最后输出 b 。
-
foo 出栈,所有执行栈内任务执行完毕。
什么是宏任务与微任务?
我们都知道 Js 是单线程都,但是一些高耗时操作就带来了进程阻塞问题。为了解决这个问题,Js 有两种任务的执行模式:
同步模式(Synchronous)和异步模式(Asynchronous)
。
在异步模式下,创建异步任务主要分为宏任务与微任务两种。ES6 规范中,
-
宏任务(Macrotask) 称为 Task, 微任务(Microtask) 称为 Jobs。
-
宏任务是由宿主(浏览器、Node)发起的,而微任务由 JS 自身发起。
宏任务与微任务的几种创建方式 👇
| 宏任务(Macrotask) | 微任务(Microtask) |
| — | — |
| setTimeout | requestAnimationFrame(有争议) |
| setInterval | MutationObserver(浏览器环境) |
| MessageChannel | Promise.[ then/catch/finally ] |
| I/O,事件队列 | process.nextTick(Node环境) |
| setImmediate(Node环境) | queueMicrotask |
| script(整体代码块) | |
注意:nextTick 队列会比 Promie 队列先执行。
如何理解 script(整体代码块)是个宏任务呢 🤔
实际上如果同时存在两个 script 代码块,会首先在执行第一个 script 代码块中的同步代码,如果这个过程中创建了微任务并进入了微任务队列,第一个 script 同步代码执行完之后,会首先去清空微任务队列,再去开启第二个 script 代码块的执行。
所以这里应该就可以理解 script(整体代码块)为什么会是宏任务。
以上概念弄明白之后,再来看循环机制是如何运行的呢?以下涉及到的任务执行顺序都是靠函数调用栈 来实现的。
-
首先,事件循环机制的是从
script
标签内的代码开始的,上边我们提到过,整个script
标签 作为一个宏任务处理的。 -
在代码执行的过程中,如果遇到宏任务,如:
setTimeout
,就会将当前任务分发到对应的执行队 列中去。 -
当执行过程中,如果遇到微任务,如:
Promise
,在创建Promise
实例对象时,代码顺序执行,如果 到了执行· then
操作,该任务就会被分发到微任务队列中去。 -
script
标签内的代码执行完毕,同时执行过程中所涉及到的宏任务也和微任务也分配到相应的队 列中去。 -
此时宏任务执行完毕,然后去微任务队列执行所有的存在的微任务。
-
微任务执行完毕,第一轮的消息循环执行完毕,页面进行一次渲染。
-
然后开始第二轮的消息循环,从宏任务队列中取出任务执行。
-
如果两个任务队列没有任务可执行了,此时所有任务执行完毕。
定时器 setTimeout
任务队列除此之外,还可以放定时器的回调函数,需要指定某些代码多少时间之后执行。
定时器主要包括两种, setTimeout 和 setInterval 两个函数
。 当我们设置定时器的时间,执行某个特定的任务,如下:
// 1 秒后执行
setTimeout(function () {
console.log(2);
}, 1000);
console.log(1)
上述的输出结果为 1, 2,执行完同步代码后,就会执行定时器中的任务事件
// 同步执行完立即执行
setTimeout(function () {
console.log(2);
}, 0);
console.log(1)
当我们执行 setTimeout(fn,0)
定时器时,会将这个定时任务回调放在任务队列的尾部,代表的含 义就是尽早的执行。
也就是等到主线程同步任务和"任务队列"现有的事件都处理完,然后才会立即执行这个定时器的任务。
上述的前提是,等到同步任务和任务队列的代码执行完毕后,如果当前代码执行很长时间,定时器并没 办法保证一定在指定时间执行。
注:HTML5 标准规定了setTimeout() 的第二个参数的最小值(最短间隔),不得低于4毫秒, 如果低于这个值,就会自动增加。
如果涉及到页面的改动,这个定时器任务通常不会立即执行,而是 16 毫秒执行一次,我们通常使用requestAnimationFrame() 。
小实战
**自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。**深知大多数前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:前端)
最后
技术是没有终点的,也是学不完的,最重要的是活着、不秃。零基础入门的时候看书还是看视频,我觉得成年人,何必做选择题呢,两个都要。喜欢看书就看书,喜欢看视频就看视频。最重要的是在自学的过程中,一定不要眼高手低,要实战,把学到的技术投入到项目当中,解决问题,之后进一步锤炼自己的技术。
技术学到手后,就要开始准备面试了,找工作的时候一定要好好准备简历,毕竟简历是找工作的敲门砖,还有就是要多做面试题,复习巩固。有需要面试题资料的朋友点击这里可以领取。
要的是活着、不秃。零基础入门的时候看书还是看视频,我觉得成年人,何必做选择题呢,两个都要。喜欢看书就看书,喜欢看视频就看视频。最重要的是在自学的过程中,一定不要眼高手低,要实战,把学到的技术投入到项目当中,解决问题,之后进一步锤炼自己的技术。
技术学到手后,就要开始准备面试了,找工作的时候一定要好好准备简历,毕竟简历是找工作的敲门砖,还有就是要多做面试题,复习巩固。有需要面试题资料的朋友点击这里可以领取。
[外链图片转存中…(img-tsr5NgKT-1713009483380)]