JS 是单线程语言。这句话对不对?

按照目前的情况来看,JS 自从支持了 Web Worker 之后,就不再是单线程语言了,但 Worker 的工作线程与主线程有区别,在 Worker 的工作线程中无法直接操作 DOM、window 对象或大多数浏览器 API(如 localStorage),Worker 的全局对象也不再是 window 对象,而是 self。

Worker 中的事件循环与主线程相互独立,互不影响,但执行顺序还是得遵循 JS 的语法规则。

宏任务

宏任务表示执行时间较长的任务,在每次时间循环时只会执行一个宏任务,执行完毕后处理微任务队列,所有微任务都执行完毕后进入下一个宏任务。

JS 常见宏任务类型:

  1. 定时器任务:setTimeout / setInterval
  2. DOM 事件回调(如 click、scroll)
  3. I/O 操作(如文件读取、网络请求)
  4. 浏览器用于执行动画的方法 requestAnimationFrame ,执行时机与渲染相关
  5. Node.js 环境的 setImmediate
  6. script 标签内主线程的同步代码(整体作为一个宏任务)

微任务

微任务表示更轻量的异步任务,当宏任务执行完毕之后立即执行。

JS 常见微任务类型:

  1. Promise.then() / Promise.catch() / Promise.finally()
  2. 浏览器监听 DOM 变化的 API 对象,比如:MutationObserver
  3. 手动添加微任务API方法:queueMicrotask()
  4. nodejs 中的 process.nextTick()

代码解析

看这么一段代码:

(function() {
console.log(1)
setTimeout(() => { console.log(2); });
queueMicrotask(() => console.log(3))
new Promise(resolve => {
console.log(4);
setTimeout(() => {
resolve();
console.log(5);
}, 0);
Promise.resolve().then(() => console.log(6));
console.log(7);
}).then(() => {
console.log(8);
Promise.resolve().then(() => console.log(9));
});
console.log(10);
})();

分析代码:

(function() {
console.log(1) // 同步任务
setTimeout(() => { console.log(2); });
queueMicrotask(() => console.log(3))
new Promise(resolve => {
console.log(4); // 同步任务
setTimeout(() => { // 宏任务
resolve(); // 宏任务的同步任务
console.log(5); // 宏任务中的同步任务
}, 0);
Promise.resolve().then(() => console.log(6)); // 微任务
console.log(7); // 同步任务
}).then(() => { // 微任务
console.log(8); // 微任务中的同步任务
Promise.resolve().then(() => console.log(9)); // 微任务中的微任务
});
console.log(10); // 同步任务
})();

第一轮

首先同步代码的宏任务优先级最高,不管微任务还是宏任务,同步代码都会先执行。

所以上面代码会优先执行:

console.log(1)
console.log(4);
console.log(7);
console.log(10);

接着开始处理微任务:

queueMicrotask(() => console.log(3))
Promise.resolve().then(() => console.log(6));

微任务处理完,开始执行下一轮宏任务。

第二轮

这一轮中的宏任务只有一个 setTimeout,执行完之后由于没有微任务队列,所以直接执行下一轮宏任务。

setTimeout(() => { console.log(2); });

第三轮

这一轮的宏任务中有同步代码。

setTimeout(() => { // 宏任务
resolve(); // 宏任务的同步任务
console.log(5); // 宏任务中的同步任务
}, 0);

在执行完 resolve() 之后,会将 Promise.then 的回调函数放入微任务队列中,所以在宏任务执行完之后会开始微任务:

then(() => { // 微任务
console.log(8); // 微任务中的同步任务
Promise.resolve().then(() => console.log(9)); // 微任务中的微任务
})

最终的打印顺序

1
4
7
10
3
6
2
5
8
9

执行流程图

JS 代码逐行执行,在遇到宏任务时,整个代码块丢到宏任务队列,在遇到微任务时,将微任务丢到本次事件循环中的微任务队列,本次事件循环执行完之后,再执行微任务队列中的任务,微任务执行完之后开始下一个宏任务执行。

JS 代码执行机制:

宏任务执行机制:

写在最后

JS 中的代码执行流程永远都是事件循环机制,这是 JS 的任务调度核心,理解事件循环机制,才能在开发中游刃有余~~

Web前端入门第 68 问:JavaScript 事件循环机制中的微任务与宏任务的更多相关文章

  1. 一篇文章图文并茂地带你轻松学完 JavaScript 事件循环机制(event loop)

    JavaScript 事件循环机制 (event loop) 本篇文章已经默认你有了基础的 ES6 和 javascript语法 知识. 本篇文章比较细致,如果已经对同步异步,单线程等概念比较熟悉的读 ...

  2. 深入理解JavaScript事件循环机制

    前言 众所周知,JavaScript 是一门单线程语言,虽然在 html5 中提出了 Web-Worker ,但这并未改变 JavaScript 是单线程这一核心.可看HTML规范中的这段话: To ...

  3. 深入浅出Javascript事件循环机制

    一.JS单线程.异步.同步概念 众所周知,JS是单线程(如果一个线程删DOM,一个线程增DOM,浏览器傻逼了-所以只能单着了),虽然有webworker酱紫的多线程出现,但也是在主线程的控制下.web ...

  4. javascript事件循环机制 浅尝手记

    引入 众所周知Javascript是一个单线程的机制,虽然可以依托多线程的浏览器实现页面如何实现页面复杂的渲染.事件响应,但仍不会改变其单线程的本质:所以对于js的事件循环机制的了解是一个前端人员的必 ...

  5. 浏览器中的JavaScript事件循环机制

    浏览器的事件循环机制是HTML中定义的规范. JavaScript有一个主线程和调用栈,所有的任务都会被放到调用栈等待主线程执行. JS调用栈 是一种先进后出的数据结构.当函数被调用时,会被添加到栈中 ...

  6. javaScript 事件循环机制

    JavaScript是单线程的编程语言,只能同一时间内做一件事.但是在遇到异步事件的时候,js线程并没有阻塞,还会继续执行,这就是因为JS有事件循环机制. 事件循环流程总结 主线程开始执行一段代码, ...

  7. JavaScript事件循环机制

    事件循环 事件循环不仅仅包含事件队列,而是具有至少两个队列,除了事件,还要保持浏览器执行的其他操作.这些操作被称为任务,并且分为两类:宏任务(或通常称为任务)和微任务. 单次循环迭代中,最多处理一个宏 ...

  8. JS JavaScript事件循环机制

    区分进程和线程 进程是cpu资源分配的最小单位(系统会给它分配内存) 不同的进程之间是可以同学的,如管道.FIFO(命名管道).消息队列 一个进程里有单个或多个线程 浏览器是多进程的,因为系统给它的进 ...

  9. 一道面试题引发对javascript事件循环机制(Event Loop)的 思考(这里讨论针对浏览器)

  10. JS浏览器事件循环机制

    文章来自我的 github 博客,包括技术输出和学习笔记,欢迎star. 先来明白些概念性内容. 进程.线程 进程是系统分配的独立资源,是 CPU 资源分配的基本单位,进程是由一个或者多个线程组成的. ...

随机推荐

  1. centos7 挂载未分配的硬盘空间 (测试可用)

    =============================================== 2019/7/28_第1次修改                       ccb_warlock == ...

  2. HTML5 给网站添加图标

    1.首先将图标上传到对应的目录下 2.在网页的index.html,添加已下代码到<head>标签里 <link rel="icon" href="i_ ...

  3. 【Python】PDF文档导出指定章节为TXT

    PDF文档导出指定章节为TXT 需求 要导出3000多个pdf文档的特定章节内容为txt格式(pdf文字可复制). 解决 导出PDF 查了一下Python操作PDF文档的方法,主要是通过3个库,PyP ...

  4. 【FAQ】HarmonyOS SDK 闭源开放能力 —Push Kit(12)

    1.问题描述: pushdeviceid的长度是固定的吗? 解决方案: 在鸿蒙系统中,设备ID的长度是固定的. 2.问题描述: 通过REST API三方推送IM类消息,如何实现应用处于前台时不展示三方 ...

  5. S7.Net与西门子PLC通讯——纯新手必看

     前言 本文档适合从未接触过PLC的.NET开发程序员入门查看.(其实看完了之后,PLC开发也就那样) PLC通讯入门比较难,需要关注的细节比较多.一边学习一边举一反三多思考,一定要自己创建Demo跟 ...

  6. tomcat非root用户启动

    部署远程服务器时候, 基本都是用root账户登录, 习惯上会直接使用root启动tomcat. 这样其实是有风险的, 黑客获取的权限即容器的权限, 如果容器运行权限就很高,被攻破黑客即可获取很高的权限 ...

  7. 详细介绍FutureTask类

    一.详细介绍FutureTask类 FutureTask 未来将要执行的任务对象,继承 Runnable.Future 接口,用于包装 Callable 对象,实现任务的提交 public stati ...

  8. Linux防火墙操作指令

    firewall-cmd --list-ports # 查看开放的端口号 firewall-cmd --zone=public --add-port=8888/tcp --permanent # 开放 ...

  9. jstree上手文档 [初始化时默认选中、全部展开、获取实例的数据等问题]

    jstree官网:https://www.jstree.com/ ------------------- 实例化tree .jstree() 默认样式: var container = $('#xxx ...

  10. 题解:CF280B Maximum Xor Secondary

    由于正求次大值比较困难,不如逆向思考. 由次大值来找最大值,即对于每个 iii,找到一个 jjj,满足 j<ij<ij<i 并且 ai<aja_i<a_jai​<a ...