详解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 相关文章推荐
struts2+jquery+json实现异步加载数据(自写)
Jun 24 Javascript
js 图片随机不定向浮动的实现代码
Jul 02 Javascript
jQuery+ajax实现动态执行脚本的方法
Jan 27 Javascript
学习JavaScript设计模式之单例模式
Jan 19 Javascript
学习掌握JavaScript中this的使用技巧
Aug 29 Javascript
js监听input输入框值的实时变化实例
Jan 26 Javascript
Javascript中类式继承和原型式继承的实现方法和区别之处
Apr 25 Javascript
JavaScript实现二叉树定义、遍历及查找的方法详解
Dec 20 Javascript
JavaScript使用prototype原型实现的封装继承多态示例
Aug 31 Javascript
浅谈layui使用模板引擎动态渲染元素要注意的问题
Sep 14 Javascript
Vue状态模式实现窗口停靠功能(灵动、自由, 管理后台Admin界面)
Mar 06 Javascript
微信小程序scroll-view点击项自动居中效果的实现
Mar 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获取地址栏信息的代码
2008/10/08 PHP
PHP 文件缓存的性能测试
2010/04/25 PHP
php获取文件夹路径内的图片以及分页显示示例
2014/03/11 PHP
摘自织梦CMS的HTTP文件下载类
2015/08/08 PHP
PHP内置加密函数详解
2016/11/20 PHP
php Session无效分析资料整理
2016/11/29 PHP
js option删除代码集合
2008/11/12 Javascript
input输入框的自动匹配(原生代码)
2013/03/19 Javascript
jQuery图片轮播的具体实现
2013/09/11 Javascript
js有序数组的连接问题
2013/10/01 Javascript
js 实现省市区三级联动菜单效果
2017/02/20 Javascript
手把手教你把nodejs部署到linux上跑出hello world
2017/06/19 NodeJs
laydate 显示结束时间不小于开始时间的实例
2017/08/11 Javascript
jquery实现企业定位式导航效果
2018/01/01 jQuery
vue多页面开发和打包正确处理方法
2018/04/20 Javascript
Babel 入门教程学习笔记
2018/06/13 Javascript
vue.js中proxyTable 转发请求的实现方法
2018/09/20 Javascript
微信小程序实现折线图的示例代码
2019/06/07 Javascript
微信小程序防止多次点击跳转(函数节流)
2019/09/19 Javascript
解决vue+router路由跳转不起作用的一项原因
2020/07/19 Javascript
Vue的v-model的几种修饰符.lazy,.number和.trim的用法说明
2020/08/05 Javascript
[44:39]2014 DOTA2国际邀请赛中国区预选赛 NE VS CNB
2014/05/21 DOTA
Python内存读写操作示例
2018/07/18 Python
python+selenium实现简历自动刷新的示例代码
2019/05/20 Python
python抓取多种类型的页面方法实例
2019/11/20 Python
美国大型的健身社区和补充商店:Bodybuilding.com
2016/09/06 全球购物
在校生汽车维修实习自我鉴定
2013/09/19 职场文书
师范毕业生个人求职信
2013/12/09 职场文书
中秋晚会策划方案
2014/06/12 职场文书
寝室长工作失责检讨书
2014/10/06 职场文书
2014年个人工作总结报告
2014/11/27 职场文书
大学生助学金感谢信
2015/01/21 职场文书
2015年档案管理员工作总结
2015/05/13 职场文书
2015年入党积极分子培养考察意见
2015/08/12 职场文书
2019个人工作态度自我评价
2019/04/24 职场文书
关于springboot 配置date字段返回时间戳的问题
2021/07/25 Java/Android