详解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 js 获取时间差、时间格式具体代码
Jun 05 Javascript
jquery验证手机号码、邮箱格式是否正确示例代码
Jul 28 Javascript
jQuery替换字符串(实例代码)
Nov 13 Javascript
多选列表框动态添加,移动,删除,全选等操作的简单实例
Jan 13 Javascript
javascript判断移动端访问设备并解析对应CSS的方法
Feb 05 Javascript
angularJS中router的使用指南
Feb 09 Javascript
JavaScript制作淘宝星级评分效果的思路
Jun 23 Javascript
简单几步实现返回顶部效果
Dec 05 Javascript
使用BootStrap实现表格隔行变色及hover变色并在需要时出现滚动条
Jan 04 Javascript
vue项目打包后怎样优雅的解决跨域
May 26 Javascript
vue-resource:jsonp请求百度搜索的接口示例
Nov 09 Javascript
js+css3实现炫酷时钟
Aug 18 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数组的使用方法小结
2010/09/23 PHP
老生常谈PHP面向对象之命令模式(必看篇)
2017/05/24 PHP
PHP的PDO预处理语句与存储过程
2019/01/27 PHP
用js实现多域名不同文件的调用方法
2007/01/12 Javascript
漂亮的widgets,支持换肤和后期开发新皮肤
2007/04/23 Javascript
js控制的回到页面顶端goTop的代码实现
2013/03/20 Javascript
JS 获取滚动条高度示例代码
2013/10/24 Javascript
nodejs初步体验篇
2015/11/23 NodeJs
jquery.serialize() 函数语法及简单实例
2016/07/08 Javascript
JS实现的简单轮播图运动效果示例
2016/12/22 Javascript
ajax分页效果(bootstrap模态框)
2017/01/23 Javascript
vue 打包后的文件部署到express服务器上的方法
2017/08/09 Javascript
jQuery选择器中的特殊符号处理方法
2017/09/08 jQuery
使用watch监听路由变化和watch监听对象的实例
2018/02/24 Javascript
webpack 3.X学习之多页面打包的方法
2018/09/04 Javascript
React router动态加载组件之适配器模式的应用详解
2018/09/12 Javascript
vue-cli3+typescript初体验小结
2019/02/28 Javascript
Vue发布项目实例讲解
2019/07/17 Javascript
如何HttpServletRequest文件对象并储存
2020/08/14 Javascript
[01:11]辉夜杯战队访谈宣传片—CDEC.Y
2015/12/26 DOTA
[50:27]OG vs LGD 2018国际邀请赛淘汰赛BO3 第一场 8.26
2018/08/30 DOTA
python判断字符串是否纯数字的方法
2014/11/19 Python
python继承和抽象类的实现方法
2015/01/14 Python
python实现读取并显示图片的两种方法
2017/01/13 Python
Collatz 序列、逗号代码、字符图网格实例
2017/06/22 Python
Python简单实现socket信息发送与监听功能示例
2018/01/03 Python
python解释器spython使用及原理解析
2019/08/24 Python
详解python中index()、find()方法
2019/08/29 Python
Python pandas如何向excel添加数据
2020/05/22 Python
Python爬取某平台短视频的方法
2021/02/08 Python
业务助理岗位职责
2013/11/18 职场文书
经济贸易专业自荐信
2014/06/11 职场文书
学习党的群众路线实践活动思想汇报
2014/09/12 职场文书
组织委员竞选稿
2015/11/21 职场文书
Hive日期格式转换方法总结
2022/06/25 数据库
浅谈MySql update会锁定哪些范围的数据
2022/06/25 MySQL