深入理解JS执行机制

简介: 深入理解JS执行机制

前言            

JavaScript是一门单线程的非阻塞脚本语言,同一时刻只允许一个代码段执行。在单线程的机制下,执行异步任务时,在等待结果返回的这个时间段,后面的代码就无法执行了。


JS在执行代码时,遇到异步任务之后还有同步任务的场景时,它并不会等待异步任务执行完,而是先执行同步任务,那么JS是如何做到这一点的呢?


本篇文章将详细讲解上述问题,欢迎各位感兴趣的开发者阅读本文。


事件循环


单线程


讲事件循环之前,我们先来理解下为什么JS不设计成多线程的。


我们做个假设,如果JS是多线程的,因为JS有DOM API可以操作DOM,此时如果有两个线程在操作同一个DOM,线程1删除了这个dom节点,线程2要操作这个dom,就会产生矛盾,到底以哪个线程为主。


为了避免这种情况的出现,JS就被设计成了单线程


宏任务与微任务


JS引擎把所有任务分为两类:宏任务、微任务。


宏任务有:


  • script整体代码
  • setTimeout、setInterval
  • I/O
  • UI渲染
  • postMessage
  • MessageChannel
  • requestAnimationFrame
  • setImmediate(Node.js 环境)


微任务有:


  • new Promise.then()
  • MutaionObserver
  • process.nextTick(Node.js 环境)


执行规则


文章一开头我们了解到了单线程的弊端,JS是通过事件循环机制(EventLoop)来解决这一弊端的,接下来我们来看下EventLoop的执行规则:


  • 所有代码作为宏任务进入主线程执行栈,开始执行
  • 执行过程中,同步代码会立即执行,宏任务进入宏任务队列,微任务进入微任务队列
  • 当前宏任务执行完成出队,读取微任务队列,有则执行,直至全部执行完毕
  • 执行浏览器ui进程渲染
  • 检查是否有webworker任务,有则执行
  • 本轮宏任务执行完成,回到第2步,继续执行,直至宏任务与微任务队列全部清空


举例说明


我们了解完它的执行规则后,接下来我们举个例子来说明下,如下所示:


console.log("1"); // 1 同步代码:立即执行 [1]
setTimeout(function() {
  console.log("2"); // 3 同步代码执行执行 输出2
  process.nextTick(function() {
    console.log("3"); // 4 进入微任务队列 [3]
  });
  new Promise(function(resolve) {
    console.log("4"); // 3 同步代码执行执行 输出4
    resolve();
  }).then(function() {
    console.log("5"); // 4 进入微任务队列 [3, 5]
  });
});
process.nextTick(function() {
  console.log("6"); // 2 进入微任务队列 [6]
});
new Promise(function(resolve) {
  console.log("7"); // 1 宏任务:立即执行 [1, 7]
  resolve();
}).then(function() {
  console.log("8"); // 2 进入微任务队列 [6, 8]
});
setTimeout(function() {
  console.log("9"); // 5 宏任务:立即执行 [9]
  process.nextTick(function() {
    console.log("10"); // 6 进入微任务队列 [10]
  });
  new Promise(function(resolve) {
    console.log("11"); // 5 宏任务:立即执行 [9, 11]
    resolve();
  }).then(function() {
    console.log("12"); // 6 进入微任务队列 [10, 12]
  });
});
// 执行顺序:1 7 6 8 2 4 3 5 9 11 10 12


process.nextTick()为node中的方法,你可以把它理解为与promise一样的微任务,promise的executor函数中的同步代码会立即执行。


我们来分析下上述代码的执行顺序,如下图所示:

640.png

                                      image.png


运行结果如下所示:

640.png

                                            image.png


当你把上述示例代码啃透后,那么你也就理解js的事件循环机制了。

当然,你可能没有那么快就啃透这个例子,这种概念性的东西,掌握它最好的办法就是:将示例代码放到编辑器里,对照着事件循环的执行规则,一行一行的去读代码,大脑过一遍,猜测运行结果,然后再去执行代码判断执行结果是否与你猜的一致。

最后,举一反三,去网上找一些事件循环的面试题多加练习,慢慢的你就把这个知识点啃透了,Good Luck!


代码地址


本文为《JS原理学习》系列的第5篇文章,本系列的完整路线请移步:JS原理学习 (1) 》学习路线规划


本系列文章的所有示例代码,请移步:js-learning


写在最后


至此,文章就分享完毕了。


我是神奇的程序员,一位前端开发工程师。


如果你对我感兴趣,请移步我的个人网站(www.kaisir.cn),进一步了解。


  • 公众号无法外链,如果文中有链接,可点击下方阅读原文查看😊
相关文章
|
6天前
|
JavaScript 安全 前端开发
乾坤js隔离机制
乾坤js隔离机制
|
5月前
|
前端开发 JavaScript UED
深入理解JavaScript中的事件循环机制
JavaScript中的事件循环机制是其异步编程的核心,深入理解该机制对于开发高效、流畅的前端应用至关重要。本文将介绍事件循环的工作原理、常见的事件循环模型,以及如何利用这些知识解决前端开发中的常见问题。
|
6天前
|
缓存 JavaScript 前端开发
常见的 JavaScript 隔离机制
常见的 JavaScript 隔离机制
|
2月前
|
JavaScript 前端开发 算法
js 内存回收机制
【8月更文挑战第23天】js 内存回收机制
33 3
|
2月前
|
存储 JavaScript 前端开发
学习JavaScript 内存机制
【8月更文挑战第23天】学习JavaScript 内存机制
27 3
|
2月前
|
JavaScript 中间件 开发者
深入浅出Node.js中间件机制
【8月更文挑战第31天】本文将带你领略Node.js中间件的奥秘,通过直观的案例分析,揭示其背后的设计哲学。你将学会如何运用中间件构建强大而灵活的后端应用,以及在面对复杂业务逻辑时如何保持代码的清晰与高效。
|
2月前
|
设计模式 JavaScript 中间件
深入浅出Node.js中间件机制
【8月更文挑战第31天】在Node.js的世界里,中间件如同魔法般存在,它让复杂的请求处理变得井然有序。本文将带你领略中间件的奥秘,从原理到实战,一步步揭开它的神秘面纱。你将学会如何运用中间件来构建强大而灵活的后端应用,就像拼乐高一样有趣。
|
4月前
|
设计模式 JavaScript 前端开发
【JavaScript】深入浅出JavaScript继承机制:解密原型、原型链与面向对象实战攻略
JavaScript的继承机制基于原型链,它定义了对象属性和方法的查找规则。每个对象都有一个原型,通过原型链,对象能访问到构造函数原型上的方法。例如`Animal.prototype`上的`speak`方法可被`Animal`实例访问。原型链的尽头是`Object.prototype`,其`[[Prototype]]`为`null`。继承方式包括原型链继承(通过`Object.create`)、构造函数继承(使用`call`或`apply`)和组合继承(结合两者)。ES6的`class`语法是语法糖,但底层仍基于原型。继承选择应根据需求,理解原型链原理对JavaScript面向对象编程至关重要
97 7
【JavaScript】深入浅出JavaScript继承机制:解密原型、原型链与面向对象实战攻略
|
3月前
|
JavaScript 前端开发 API
js 运行机制(含异步机制、同步任务、异步任务、宏任务、微任务、Event Loop)
js 运行机制(含异步机制、同步任务、异步任务、宏任务、微任务、Event Loop)
32 0
下一篇
无影云桌面