详解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 相关文章推荐
加载jQuery后$冲突的解决办法
Jul 09 Javascript
jquery 学习之一 对象访问
Nov 23 Javascript
灵活应用js调试技巧解决样式问题的步骤分享
Mar 15 Javascript
用Javascript获取页面元素的具体位置
Dec 09 Javascript
JavaScript中奇葩的假值示例应用
Mar 11 Javascript
Javascript学习笔记之 函数篇(一) : 函数声明和函数表达式
Jun 24 Javascript
jQuery中:contains选择器用法实例
Dec 30 Javascript
如何在Angular.JS中接收并下载PDF
Nov 26 Javascript
JavaScript运动框架 多物体任意值运动(三)
May 17 Javascript
vue脚手架中配置Sass的方法
Jan 04 Javascript
浅谈vue引用静态资源需要注意的事项
Sep 28 Javascript
vue/cli 配置动态代理无需重启服务的方法
May 20 Vue.js
鸿蒙系统中的 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+mysql一个名片库程序
2006/10/09 PHP
php使用ftp实现文件上传与下载功能
2017/07/21 PHP
PHP四种排序算法实现及效率分析【冒泡排序,插入排序,选择排序和快速排序】
2018/04/27 PHP
利用javascript/jquery对上传文件格式过滤的方法
2009/07/25 Javascript
纯js实现瀑布流展现照片(自动适应窗口大小)
2013/04/08 Javascript
Javascript表格翻页效果的具体实现
2013/10/05 Javascript
JS复制到剪贴板示例代码
2013/10/30 Javascript
查看大图功能代码jquery版
2013/11/05 Javascript
JSONP获取Twitter和Facebook文章数的具体步骤
2014/02/24 Javascript
jQuery中的Deferred和promise 的区别
2016/04/03 Javascript
js调用屏幕宽度的简单方法
2016/11/14 Javascript
vue实现验证码按钮倒计时功能
2018/04/10 Javascript
使用sessionStorage解决vuex在页面刷新后数据被清除的问题
2018/04/13 Javascript
nodejs express配置自签名https服务器的方法
2018/05/22 NodeJs
H5+C3+JS实现双人对战五子棋游戏(UI篇)
2020/05/28 Javascript
layui 实现表格某一列显示图标
2019/09/19 Javascript
小程序使用wxs解决wxml保留2位小数问题
2019/12/13 Javascript
python字典多条件排序方法实例
2014/06/30 Python
Python中字符串格式化str.format的详细介绍
2017/02/17 Python
利用Hyperic调用Python实现进程守护
2018/01/02 Python
Python并发之多进程的方法实例代码
2018/08/15 Python
利用python在excel里面直接使用sql函数的方法
2019/02/08 Python
Python中判断子串存在的性能比较及分析总结
2019/06/23 Python
python实现在内存中读写str和二进制数据代码
2020/04/24 Python
学python爬虫能做什么
2020/07/29 Python
利用pipenv和pyenv管理多个相互独立的Python虚拟开发环境
2020/11/01 Python
html5 svg 中元素点击事件添加方法
2013/01/16 HTML / CSS
北美三大旅游网站之一:Travelocity加拿大
2016/08/20 全球购物
台湾前三大B2C购物网站:MOMO购物网
2017/04/27 全球购物
查摆问题自我剖析材料
2014/08/18 职场文书
大学教师师德师风演讲稿
2014/08/22 职场文书
机关干部四风问题自我剖析及整改措施
2014/10/26 职场文书
党员剖析材料范文
2014/12/18 职场文书
golang日志包logger的用法详解
2021/05/05 Golang
MySQL笔记 —SQL运算符
2022/01/18 MySQL
Python内置包对JSON文件数据进行编码和解码
2022/04/12 Python