详解JavaScript 的执行机制


Posted in Javascript onSeptember 18, 2020

一、关于javascript

javascript是一门单线程语言,在最新的HTML5中提出了Web Worker,但javascript是单线程这一核心仍未改变。

为什么js是单线程的语言?因为最初的js是用来在浏览器验证表单操纵DOM元素的。如果js是多线程的话,两个线程同时对一个DOM进行了相互冲突的操作,那么浏览器的解析是无法执行的。

Web Worker 的作用,就是为 JavaScript 创造多线程环境,允许主线程创建 Worker 线程,将一些任务分配给后者运行。在主线程运行的同时,Worker 线程在后台运行,两者互不干扰。等到 Worker 线程完成计算任务,再把结果返回给主线程。

二、javascript事件循环

当我们调用一个方法的时候,js会生成一个与这个方法对应的执行环境(context),又叫执行上下文。这个执行环境中存在着这个方法的私有作用域,上层作用域的指向,方法的参数,这个作用域中定义的变量以及这个作用域的this对象。 而当一系列方法被依次调用的时候,因为js是单线程的,同一时间只能执行一个方法,于是这些方法被排队在一个单独的地方。这个地方被称为执行栈。

js引擎遇到一个异步事件后并不会一直等待其返回结果,而是会将这个事件挂起,继续执行执行栈中的其他任务。

当一个异步事件返回结果后,js会将这个事件加入与当前执行栈不同的另一个队列,我们称之为事件队列。被放入事件队列不会立刻执行其回调,而是等待当前执行栈中的所有任务都执行完毕, 主线程处于闲置状态时,主线程会去查找事件队列是否有任务。如果有,那么主线程会从中取出排在第一位的事件,并把这个事件对应的回调放入执行栈中,然后执行其中的同步代码。如此反复,这样就形成了一个无限的循环。

三、setTimeout

setTimeout这个函数,是经过指定时间后,把要执行的任务加入到Event Queue中,又因为是单线程任务要一个一个执行,如果前面的任务需要的时间太久,那么只能等着。

setTimeout(() => {
 task()
},3000)

sleep(10000000)

上述代码执行task()需要的时间远远超过3秒,执行过程如下:

  • task()进入Event Table并注册,计时开始。
  • 执行sleep函数,很慢,非常慢,计时仍在继续。
  • 3秒到了,计时事件timeout完成,task()进入Event Queue,但是sleep还没执行完,只好等着。
  • sleep终于执行完了,task()终于从Event Queue进入了主线程执行。

  setTimeout(fn,0)的含义是,指定某个任务在主线程最早可得的空闲时间执行,意思就是不用再等多少秒了,只要主线程执行栈内的同步任务全部执行完成,栈为空就马上执行。

四、setInterval

   对于执行顺序来说,setInterval会每隔指定的时间将注册的函数置入Event Queue,如果前面的任务耗时太久,那么同样需要等待。

唯一需要注意的一点是,对于setInterval(fn,ms)来说,我们已经知道不是每过ms秒会执行一次fn,而是每过ms秒,会有fn进入Event Queue。

五、Promise与process.nextTick(callback)

Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。

所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。

process.nextTick(callback)类似node.js版的"setTimeout",在事件循环的下一次循环中调用 callback 回调函数。

六、宏任务和微任务

  • macro-task(宏任务):包括整体代码script,setTimeout,setInterval
  • micro-task(微任务):Promise.then,process.nextTick

不同类型的任务会进入对应的Event Queue,比如setTimeout和setInterval会进入相同的Event Queue。

事件循环的顺序,决定js代码的执行顺序。

进入整体代码(宏任务)后,开始第一次循环。接着执行所有的微任务。然后再次从宏任务开始,找到其中一个任务队列执行完毕,再执行所有的微任务。

setTimeout(function() {
 console.log('setTimeout');
})

new Promise(function(resolve) {
 console.log('promise');
}).then(function() {
 console.log('then');
})

console.log('console');
  • 这段代码作为宏任务,进入主线程。
  • 先遇到setTimeout,那么将其回调函数注册后分发到宏任务Event Queue。
  • 接下来遇到了Promise,new Promise立即执行,因为new Promise回调函数中的代码是同步任务,then函数分发到微任务Event Queue。
  • 遇到console.log(),立即执行。
  • 好啦,整体代码script作为第一个宏任务执行结束,看看有哪些微任务?我们发现了then在微任务Event Queue里面,执行。
  • 第一轮事件循环结束了,我们开始第二轮循环,当然要从宏任务Event Queue开始。我们发现了宏任务Event Queue中setTimeout对应的回调函数,立即执行。
  • 结束。

七、async await

1.async 做一件什么事情?

带 async 关键字的函数,它使得你的函数的返回值必定是 promise 对象。

也就是,如果async关键字函数返回的不是promise,会自动用 Promise.resolve() 包装。如果async关键字函数显式地返回promise,那就以你返回的promise为准。

2.await 在等什么?

await等的是右侧「表达式」的结果。也就是说,右侧如果是函数,那么函数的return值就是「表达式的结果」。右侧如果是一个 'hello' 或者什么值,那表达式的结果就是 'hello'。

3.await 等到之后,做了一件什么事情?

await右侧表达式的结果,就是await要等的东西。等到之后,对于await来说,分2个情况:

  • 不是promise对象
  • 是promise对象

如果不是 promise , await会阻塞后面的代码,先执行async外面的同步代码,同步代码执行完,再回到async内部,把这个非promise的东西,作为 await表达式的结果。

如果它等到的是一个 promise 对象,await 也会暂停async后面的代码,先执行async外面的同步代码,等着 Promise 对象 fulfilled,然后把 resolve 的参数作为 await 表达式的运算结果。

4.示例

async function async1() {
 console.log('async1 start');
 await async2();
 console.log('async1 end');
}

async function async2() {
 console.log('async2');
}

console.log('script start');
setTimeout(function () {
 console.log('setTimeout');
}, 0);
async1();
new Promise(function (resolve) {
 console.log('promise1');
 resolve();
}).then(function () {
 console.log('promise2');
});
console.log('script end');
  • 这段代码作为宏任务,进入主线程。
  • 先打印出script start。接着执行函数async1。
  • 打印出async1 start,执行到await,执行函数async2,打印出async2。
  • 此时await会阻塞async1后面的代码,会先执行async1外面的同步代码。
  • setTimeout放入事件循环的宏任务。接着执行到Promise,打印出promise1,promise.then放入事件循环的微任务。接着打印script end。
  • 现在async1外面的同步代码执行完毕,回到async1内部打印出async1 end。
  • 整体代码执行完毕,执行微任务promise.then,打印出promise2。
  • 最后执行宏任务setTimeout,打印setTimeout。

所以这段代码的执行顺序为:script start -> async1 start -> async2 -> promise1 -> script end -> async1 end -> promise2 -> setTimeout

以上就是详解JavaScript 的执行机制的详细内容,更多关于JavaScript 执行机制的资料请关注三水点靠木其它相关文章!

Javascript 相关文章推荐
有效的捕获JavaScript焦点的方法小结
Oct 08 Javascript
js的alert样式如何更改如背景颜色
Jan 22 Javascript
解决jQuery动态获取手机屏幕高和宽的问题
May 07 Javascript
jquery动态分页效果堪比时光网
Sep 25 Javascript
JavaScript对象学习小结
Sep 02 Javascript
基于react组件之间的参数传递(详解)
Sep 05 Javascript
React教程之封装一个Portal可复用组件的方法
Jan 02 Javascript
12条写出高质量JS代码的方法
Jan 07 Javascript
JS运动特效之链式运动分析
Jan 24 Javascript
如何使用vuex实现兄弟组件通信
Nov 02 Javascript
如何利用node.js开发一个生成逐帧动画的小工具
Dec 01 Javascript
JavaScript实现HTML导航栏下拉菜单
Nov 25 Javascript
鸿蒙系统中的 JS 开发框架
Sep 18 #Javascript
React倒计时功能实现代码——解耦通用
Sep 18 #Javascript
浅谈鸿蒙 JavaScript GUI 技术栈
Sep 17 #Javascript
vue项目中播放rtmp视频文件流的方法
Sep 17 #Javascript
逐行分析鸿蒙系统的 JavaScript 框架(推荐)
Sep 17 #Javascript
vue项目实现多语言切换的思路
Sep 17 #Javascript
vue实现放大镜效果
Sep 17 #Javascript
You might like
使用PHP强制下载PDF文件示例
2014/01/17 PHP
php解决抢购秒杀抽奖等大流量并发入库导致的库存负数的问题
2014/06/19 PHP
php缩放gif和png图透明背景变成黑色的解决方法
2014/10/14 PHP
Joomla语言翻译类Jtext用法分析
2016/05/05 PHP
centos 7.2下搭建LNMP环境教程
2016/11/20 PHP
PHP实践教程之过滤、验证、转义与密码详解
2017/07/24 PHP
解密效果
2006/06/23 Javascript
[JS]点出统计器
2020/10/11 Javascript
JavaScipt基本教程之前言
2008/01/16 Javascript
不同浏览器的怪癖小结
2010/07/11 Javascript
jquery中ajax函数执行顺序问题之如何设置同步
2014/02/28 Javascript
jQuery实现自动调整字体大小的方法
2015/06/15 Javascript
jQuery中Find选择器用法示例
2016/09/21 Javascript
jquery结合html实现中英文页面切换
2016/11/29 Javascript
jQuery无刷新上传之uploadify简单代码
2017/01/17 Javascript
JS中showModalDialog关闭子窗口刷新主窗口用法详解
2017/03/25 Javascript
JavaScript继承定义与用法实践分析
2018/05/28 Javascript
Vue CLI4 Vue.config.js标准配置(最全注释)
2020/06/05 Javascript
Python中的命令行参数解析工具之docopt详解
2017/03/27 Python
pandas 读取各种格式文件的方法
2018/06/22 Python
python 堆和优先队列的使用详解
2019/03/05 Python
python并发编程多进程 模拟抢票实现过程
2019/08/20 Python
python实现的接收邮件功能示例【基于网易POP3服务器】
2019/09/11 Python
如何用OpenCV -python3实现视频物体追踪
2019/12/04 Python
解决pyPdf和pyPdf2在合并pdf时出现异常的问题
2020/04/03 Python
Python实现汇率转换操作
2020/05/03 Python
Windows下pycharm安装第三方库失败(通用解决方案)
2020/09/17 Python
python实现定时发送邮件
2020/12/23 Python
Perry Ellis官网:美国男士品味服装
2016/12/09 全球购物
家长对孩子评语
2014/01/30 职场文书
艺人经纪人岗位职责
2014/04/15 职场文书
工作检讨书范文
2015/01/23 职场文书
因家庭原因离职的辞职信范文
2015/05/12 职场文书
迁徙的鸟观后感
2015/06/09 职场文书
社区低保工作总结2015
2015/07/23 职场文书
SpringBoot2 参数管理实践之入参出参与校验的方式
2021/06/16 Java/Android