面试中常见的问题:JavaScript 宏任务与微任务,包教包会

事件循环Event Loop

我们都知道,JavaScript 是一种单线程的编程语言,简单的说就是:js只有一条通道,那么在任务多的情况下,就会出现拥挤的情况,这种情况下就产生了 ‘多线程’ ,但是这种“多线程”是通过单线程模仿的,也就是假的。

这意味着它一次只能执行一个任务。但是,通过事件循环(Event Loop)机制,可以处理异步操作并确保程序的高效运行。理解事件循环对于我们前端开发者来说至关重要!

事件循环的基本概念

事件循环的核心在于任务队列(Task Queue),它负责管理和调度各种任务的执行。任务分为两类:宏任务微任务

事件循环的执行步骤

事件循环的基本执行步骤如下:

  1. 执行一个宏任务(例如,从头到尾运行一个 script)。
  2. 执行所有的微任务(一直执行,直到微任务队列为空)。
  3. 更新渲染(如果需要)。
  4. 继续执行下一个宏任务。

看不懂没关系,结合下面的例子就能明白了!

宏任务和微任务详解

基于上面的基础知识,我们详细介绍下宏任务和微任务

为什么要区分宏任务和微任务?

(1)js是单线程的,但是分同步异步
(2)微任务和宏任务皆为异步任务,它们都属于一个队列
(3)宏任务一般是:script、setTimeout、setInterval、postMessage、MessageChannel、setImmediate(Node.js 环境)
(4)微任务:Promise.then、Object.observe、MutationObserver、process.nextTick(Node.js 环境)
(5)先执行同步再执行异步,异步遇到微任务,先执行微任务,执行完后如果没有微任务,就执行下一个宏任务,如果有微任务,就按顺序一个一个执行微任务

宏任务、微任务有哪些?

  • 宏任务一般是:script、setTimeout、setInterval、postMessage、MessageChannel、setImmediate(Node.js 环境)
  • 微任务:Promise.then、Object.observe、MutationObserver、process.nextTick(Node.js 环境)

宏任务、微任务是怎么执行的?

简单来说,宏任务、微任务的执行顺序是:先执行同步代码,异步任务放入任务队列中,当所有同步代码执行完毕后,先执行微任务在执行宏任务。

接下来,我们将通过很多例子,帮助大家深入了解

基本事件循环

setTimeout(function(){console.log(1);
});
new Promise(function(resolve){		    console.log(2);resolve();
}).then(function(){		    console.log(3);
}).then(function(){console.log(4)
}); 		
console.log(5);
// 2 5 3 4 1

分析

  1. setTimout是异步宏任务,放入宏任务队列中
  2. new Promise在实例化的过程中所执行的代码都是同步进行的,所以输出2
  3. Promise.then是异步微任务,将其放入微任务队列中
  4. 遇到同步任务console.log(5);输出5;主线程中同步任务执行完
  5. 从微任务队列中取出任务到主线程中,输出3、 4,微任务队列为空
  6. 从宏任务队列中取出任务到主线程中,输出1,宏任务队列为空
console.log(1)
setTimeout(function() {console.log(2)
}, 0)
const p = new Promise((resolve, reject) => {resolve(4)
})
p.then(data => {console.log(data)
})
console.log(3)
//1,3,4,2
  • 遇到同步任务console.log(1);输出1;
  • 遇到setTimeout 异步宏任务,放入宏任务队列中;
  • 遇到 Promise,new Promise在实例化的过程中所执行的代码都是同步进行的,但由于new Promise没有输出事件,所以接着执行遇到.then;
  • 执行.then,异步微任务,被分发到微任务Event Queue中;
  • 遇到同步任务console.log(3);输出3;
  • 主线程中同步任务执行完,从微任务队列中取出任务到主线程中,p.then 输出4,微任务执行完毕,任务队列为空;
  • 开始执行宏任务setTimeout 输出2,宏任务队列为空;

嵌套的宏任务与微任务

console.log('脚本开始')
setTimeout(() => {console.log('这是setTimeout1-------------');Promise.resolve().then(() => {console.log('这是promise1');});
}, 0);setTimeout(() => {console.log('这是setTimeout2');
}, 0);Promise.resolve().then(() => {console.log('这是promise2');
});console.log('脚本结束');

执行顺序:

  1. console.log('脚本开始') —— 同步代码,立即执行。
  2. 第一个 setTimeout 回调注册到宏任务队列。
  3. 第二个 setTimeout 回调注册到宏任务队列。
  4. Promise 回调注册到微任务队列。
  5. console.log('脚本结束') —— 同步代码,立即执行。
  6. 当前宏任务执行完毕,执行微任务队列:
    • console.log('这是promise2')
  7. 微任务执行完毕,执行第一个宏任务队列:
    • console.log('这是setTimeout1-------------')
    • 注册新的微任务 console.log('这是promise1') 到微任务队列。
  8. 执行微任务队列:
    • console.log('这是promise1')
  9. 执行第二个宏任务队列:
    • console.log('这是setTimeout2')

输出结果:

脚本开始
脚本结束
这是promise2
这是setTimeout1-------------
这是promise1
这是setTimeout2

多个微任务

console.log('脚本开始');setTimeout(() => {console.log('这是setTimeout');
}, 0);Promise.resolve().then(() => {console.log('这是promise1');
}).then(() => {console.log('这是promise2');
}).then(() => {console.log('这是promise3');
});console.log('脚本结束');

执行顺序:

  1. console.log('脚本开始') —— 同步代码,立即执行。
  2. setTimeout 回调注册到宏任务队列。
  3. Promise 回调注册到微任务队列。
  4. console.log('脚本结束') —— 同步代码,立即执行。
  5. 当前宏任务执行完毕,执行微任务队列:
    • console.log('这是promise1')
    • 注册新的微任务 console.log('这是promise2') 到微任务队列。
    • 执行微任务队列:
      • console.log('这是promise2')
      • 注册新的微任务 console.log('这是promise3') 到微任务队列。
      • 执行微任务队列:
        • console.log('这是promise3')
  6. 微任务执行完毕,执行宏任务队列:
    • console.log('这是setTimeout')

输出结果:

脚本开始
脚本结束
这是promise1
这是promise2
这是promise3
这是setTimeout

总结

通过这些详细的例子,相信大家对宏任务与微任务一定有了更深入的了解。希望本文对大家有帮助~感谢支持

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

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

相关文章

【LeetCode102.二叉树的层序遍历】vs.【LeetCode103.二叉树的锯齿形层序遍历】

题目链接 LeetCode102.二叉树的层序遍历:102. 二叉树的层序遍历 - 力扣(LeetCode)LeetCode103.二叉树的锯齿形层序遍历:103. 二叉树的锯齿形层序遍历 - 力扣(LeetCode) 实现思路 定义一个队列&#xff0…

Redis On-CPU Profiling定位瓶颈到可视化火焰图

1 . 前置检查:确认 CPU 真的是瓶颈 在正式打性能“补丁”前,务必跑一遍系统级健康核对表(推荐 Brendan Greg 的 USE Method):资源关注指标常用工具CPUUtil/Idle、RunQueuetop、vmstat、sar内存Fault、Swap、Cache Miss…

未来趋势:AI与量子计算对服务器安全的影响

随着技术的飞速发展,人工智能(AI)和量子计算正在深刻改变信息技术的各个领域。特别是在服务器安全领域,这两项技术既带来了新的可能性,也带来了前所未有的挑战。本文将探讨AI和量子计算技术对服务器安全的影响&#xf…

markdown学习笔记(个人向) Part.1

markdown学习笔记(个人向) Part.1 1. 推荐插件 markdown: 安装支持markdown的插件; markdown-preview-github-styles: 可以将VS Code上默认的markdown预览样式修改成github上常用的形式,很大程度上提高文件…

ZooKeeper 实现分布式锁

1. 分布式锁概述 在分布式系统中,为了保证共享资源在并发访问下的数据一致性,需要引入分布式锁。分布式锁是一种在分布式环境下控制多个进程对共享资源进行互斥访问的机制。它与单机环境下的锁(如Java中的synchronized或Lock)不同…

Linux线程——基础全解

一、什么是线程(Thread)?✅ 定义:线程是程序执行的最小单位。即线程(Thread)是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。一个进程可以并发多…

Java基础--封装+static

目录 什么是封装? 什么是访问限定符? static静态修饰符 用static修饰的类变量或类方法的注意事项: 什么是封装? 封装是面向对象的三大特性之一,指的是将一个类中的实现细节进行隐藏,对外只提供一些开放…

DAY 51 复习日

作业:day43的时候我们安排大家对自己找的数据集用简单cnn训练,现在可以尝试下借助这几天的知识来实现精度的进一步提高import torch import torch.nn as nn import torch.nn.functional as F import torchvision import torchvision.transforms as trans…

针对网络爬虫的相关法律法规整理

在中国,网络爬虫的法律法规涉及多个层面,包括个人信息保护、数据安全、网络安全、知识产权、反不正当竞争等。以下是详细的法律法规分析及合规指南: 1. 核心法律法规及适用场景​ ​​(1)《民法典》——隐私权与个人信…

1.1_5_2 计算机网络的性能指标(下)

继续来看计算机网络的性能指标,接下来我们探讨时延,时延带宽积和往返时延,以及信道利用率这几个性能指标。 首先来看时延这个性能指标,英文叫delay,也有的教材,把它翻译为延迟。所谓的时延,就是…

PP-OCRv2:超轻OCR系统的万能包

PP-OCRv2:超轻OCR系统的万能包摘要光学字符识别(OCR)系统已广泛应用于多种场景,但设计兼顾精度与效率的OCR系统仍具挑战性。我们此前提出的超轻量OCR系统PP-OCR在平衡两者方面取得进展。本文进一步提出PP-OCRv2,通过五…

常见的软件版本开源协议

开源软件许可证核心指南 一、许可证基础分类 1. 宽松型许可证(Permissive) 核心特征:允许闭源衍生,仅保留版权声明适用场景:商业集成、快速开发代表协议: 📜 MIT 📜 Apache 2.0 &…

基于FPGA的一维序列三次样条插值算法verilog实现,包含testbench

目录 1.前言 2.算法运行效果图预览 3.算法运行软件版本 4.部分核心程序 5.算法仿真参数 6.算法理论概述 7.参考文献 8.算法完整程序工程 1.前言 三次样条插值是一种在数据拟合和信号处理中广泛应用的技术,它通过构造分段三次多项式来逼近给定的离散数据点&a…

RAG 之 Prompt 动态选择的三种方式

“如果我有5个prompt模板,我想只选择一个每次都自动五选一能做到吗怎么做?” 完全可以做到。这在复杂的RAG或Agentic工作流中是一个非常普遍且关键的需求,通常被称为“条件路由(Conditional Routing)”或“动态调度&am…

【ROS2 自动驾驶学习】02-安装ROS2及其配套工具

目录 一、设置语言环境 二、添加存储库 三、添加软件源 四、安装ROS2 五、配置环境 六、测试ROS2 七、安装一些工具 7.1 terminator 7.2 colcon工具 7.3 tf工具 7.4 joint-state-publisher工具 7.5 urdf 八、安装三方库 8.1 Eigen 8.2 yaml-cpp 8.3 matplotl…

系统学习Python——并发模型和异步编程:基础知识

分类目录:《系统学习Python》总目录 并行是并发的一种特殊情况。**所有并行系统都是并发的,但不是所有并发系统都是并行的。**在21世纪初,我们可以使用单核设备在GNU Linux上同时处理100个进程。一台拥有4个CPU核的现代笔记本计算机&#xff…

睿尔曼系列机器人——以创新驱动未来,重塑智能协作新生态(下)

在智能制造与人工智能深度融合的当下,机器人技术正经历从 “功能替代” 到 “价值共创” 的深刻跃迁。睿尔曼,作为全球超轻量仿人机械臂领域的先行者,始终秉持 “让机器人触手可及” 的使命,凭借底层技术的突破性进展,…

表征工程(Representation Engineering, RepE)

表征工程(Representation Engineering, RepE) 近年来,表征工程(Representation Engineering, RepE)在提升AI系统透明度和可控性方面取得了显著进展。 一、大模型可解释性与可控性的突破 核心论文:《Representation Engineering: A Top-Down Approach to AI Transparen…

国产ARM+FPGA工业开发平台——GM-3568JHF

一、引言 随着物联网和国产替代需求的快速发展,嵌入式系统面临计算性能与硬件灵活性的双重挑战。GM-3568JHF开发板基于国产“ARMFPGA”异构架构,结合瑞芯微RK3568J处理器与紫光同创Logos-2 FPGA芯片,支持国产自主操作系统,满足通…

RISCV Linux 虚拟内存精讲系列一 Sv39

笔者认为,Linux 操作系统(Operating System)最核心的机制是虚拟内存(Virtual Memory)。因为,操作系统主要作用是将硬件环境抽象起来,给在其中运行的应用(Applications)提…