JS三座大山再学习(三、异步和单线程)

前言
写这一篇的时候,BiKaBi查阅了很多资料和文章,但是相当多的文章写的都很简单,甚至互相之间有矛盾,这让我很困扰;同时也让我坚定了要写出一篇好的关于JS异步、单线程、事件循环的文章,下面,让我们一起来学习本文吧,冲鸭~~

单线程

  1. 什么是单线程

//栗子1
console.log(1)
console.log(2)
console.log(3)
//输出顺序 1 2 3
单线程即同一时间只做一件事

  1. JavaScript为什么是单线程
    1.首先是历史原因,在创建 javascript 这门语言时,多进程多线程的架构并不流行,硬件支持并不好。
    2.其次是因为多线程的复杂性,多线程操作需要加锁,编码的复杂性会增高。
    3.而且,如果同时操作 DOM ,在多线程不加锁的情况下,最终会导致 DOM 渲染的结果不可预期

为了利用多核CPU的计算能力,HTML5提出Web Worker标准,允许JavaScript脚本创建多个线程,但是子线程完全受主线程控制,且不得操作DOM。所以,这个新标准并没有改变JavaScript单线程的本质。

异步

1.JS的 同步任务/异步任务

同步任务:在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务

异步:不进入主线程、而进入"任务队列"(task queue)的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行

//异步的栗子
console.log(1)
setTimeout(()=>{
console.log(2)
},100)
console.log(3)
//输出顺序 1 3 2

2. JavaScript为什么需要异步

如果在JS代码执行过程中,某段代码执行过久,后面的代码迟迟不能执行,产生阻塞(即卡死),会影响用户体验。

JavaScript怎么实现异步
JS 实现异步时通过 事件循环(Event Loop),下面我们来了解一下

1.执行栈与任务队列
先理解几个概念

1.JS任务 分为同步任务(synchronous)和异步任务(asynchronous)
2.同步任务都在 JS引擎线程(主线程) 上执行,形成一个执行栈(call stack)
3.事件触发线程 管理一个 任务队列(Task Queue)
4.异步任务 触发条件达成,将 回调事件 放到任务队列(Task Queue)中
5.执行栈中所有同步任务执行完毕,此时JS引擎线程空闲,系统会读取任务队列,将可运行的异步任务回调事件添加到执行栈中,开始执行

当一个JS文件第一次执行的时候,js引擎会 解析这段代码,并将其中的同步代码 ***按照执行顺序加入执行栈***中,然后从头开始执行。如果当前执行的是一个方法,那么js会向执行栈中添加这个方法的执行环境,然后进入这个执行环境继续执行其中的代码。当这个执行环境中的代码 执行完毕并返回结果后,js会退出这个执行环境并把这个执行环境销毁,回到上一个方法的执行环境。这个过程反复进行,直到执行栈中的代码全部执行完毕。

举个栗子:
//Event loop
//(1)
console.log(1)
//(2)
setTimeout(()=>{console.log(2)
},100)
//(3)
console.log(3)

1.先解析整段代码,按照顺序加入到执行栈中,从头开始执行
2.先执行(1),是同步的,所以直接打印 1
3.执行(2),发现是 setTimeout,于是调用浏览器的方法(webApi)执行,在 100ms后将 console.log(2) 加入到任务队列
4.执行(3),同步的,直接打印 3
5.执行栈已经清空了,现在检查任务队列,(执行太快的话可能此时任务队列还是空的,没到100ms,还没有将(2)的打印加到任务队列,于是不停的检测,直到队列中有任务),发现有 console.log(2),于是添加到执行栈,执行console.log(2),同步代码,直接打印 2 (如果这里是异步任务,同样会再走一遍循环:–>任务队列->执行栈)

所以结果是 1 3 2
注:setTimeout/Promise等我们称之为任务源。而进入任务队列的是他们指定的回调

2.宏任务(macro task)与微任务(micro task)

上面的循环只是一个宏观的表述,实际上异步任务之间也是有不同的,分为 宏任务(macro task)微任务(micro task),最新的标准中,他们被称为 taskjobs

宏任务有哪些:script(整体代码), setTimeout, setInterval, setImmediate, I/O, UI rendering(渲染)
微任务有哪些:process.nextTick, Promise, Object.observe(已废弃), MutationObserver(html5新特性)

下面我们再详细讲解一下执行过程

执行栈在执行的时候,会把宏任务放在一个宏任务的任务队列,把微任务放在一个微任务的任务队列,在当前执行栈为空的时候,主线程会 查看微任务队列是否有事件存在。如果微任务队列不存在,那么会去宏任务队列中 取出一个任务 加入当前执行栈;如果微任务队列存在,则会依次执行微任务队列中的所有任务,直到微任务队列为空(同样,是吧队列中的事件加到执行栈执行),然后去宏任务队列中取出最前面的一个事件加入当前执行栈…如此反复,进入循环。

注:

1.宏任务和微任务的任务队列都可以有多个
2.当前执行栈执行完毕时会立刻先处理所有微任务队列中的事件,然后再去宏任务队列中取出一个事件。同一次事件循环中,微任务永远在宏任务之前执行。
3.不同的运行环境 循环策略可能有不同,这里探讨chrome、node环境

举个栗子://Event loop//(1)
setTimeout(()=>{console.log(1)
},100)//(2)
setTimeout(()=>{console.log(2)
},100)//(3)
new Promise(function(resolve,reject){//(4)console.log(3)resolve(4)
}).then(function(val){//(5)console.log(val);
})//(6)
new Promise(function(resolve,reject){//(7)console.log(5)resolve(6)
}).then(function(val){//(8)console.log(val);
})//(9)
console.log(7)//(10)
setTimeout(()=>{console.log(8)
},50)

*上面的代码在node和chrome环境的正确打印顺序是 3 5 7 4 6 8 1 2

下面分析一下执行过程:

  1. 全部代码在解析后加入执行栈
  2. 执行(1),宏任务,调用webapi setTimeout,这个方法会在100ms后将回调函数放入宏任务的任务队列
  3. 执行(2),同(1),但是会比(1)稍后一点
  4. 执行(3),同步执行new Promise,然后执行(4),直接打印 3 ,然后resolve(4),然后.then(),把(5)放入微任务的任务队列
  5. 执行(6),同上,先打印 5 ,再执行resolve(6),然后.then()里面的内容(8)加入到微任务的任务队列
  6. 执行(9),同步代码,直接打印 7
  7. 执行(10),同(1)和(2),只是时间更短,会在 50ms 后将回调 console.log(8) 加入宏任务的任务队列
  8. 现在执行栈清空了,开始检查微任务队列,发现(5),加入到执行栈执行,是同步代码,直接打印 4
  9. 任务队列又执行完了,又检查微任务队列,发现(8),打印 6
  10. 任务队列又执行完了,检查微任务队列,没有任务,再检查宏任务队列,此时如果超过了50ms的话,会发现 console.log(8) 在宏任务队列中,于是执行 打印 8
  11. 依次打印 1 2

注:因为渲染也是宏任务,需要在一次执行栈执行完后才会执行渲染,所以如果执行栈中同时有几个同步的改变同一个样式的代码,在渲染时只会渲染最后一个

结语
写到这里,仍然觉得还有很多知识点没有写出来,但是想写又不知道从哪里入手。于是决定今天就写到这里,日后再做补充。 到这篇,JS三座大山系列就暂时完结了,在这其中自己也学到了很多,希望能继续输出一些有意义的东西,加油,BiKaBi~~

参考文章:

https://www.jianshu.com/p/12b9f73c5a4f
https://www.cnblogs.com/cangqinglang/p/8967268.html

如有错误,请斧正
以上

js三座大山----(第三座山)相关推荐

  1. “挖空三座山、装了几万台服务器”的绿色数据中心

    富士康在贵安新区的数据中心造在了山洞里."他们挖空了三座山,装了几万台服务器!" 数据中心主体竣工后,经实际测试年pue值小于1.1,居于世界先进水平. 当时,富士康规划园区中有一 ...

  2. js三座大山----(第一座山)

    一直很想了解js三座大山,面试的时候经常被问的问题,也是最基础的问题,很多写法的写过也知道,但是一直不知道这样的写法是js三座大山里的,今天通过各个地方查询,但都不是很全,现在分享给你们 ** JS三 ...

  3. js三座大山----(第二座山)

    JS三座大山再学习(二.作用域和闭包) 作用域 JS中有两种作用域:全局作用域|局部作用域 栗子1 console.log(name); //undefined var name = '波妞'; va ...

  4. (六)JS基础知识三(走进作用域和闭包)【三座大山之二,不会闭包,基本不会通过】

    JS基础知识三(作用域和闭包) 提问 作用域 自由变量 闭包 this 提问 this的不同应用场景,如何取值 手写bind函数 实际开发中闭包的应用场景,举例说明 创建10个a标签,点击的时候弹出对 ...

  5. 三座”大山“或让谷歌无人机送货项目被冻结

    [阅读原文] 作者:楠沨 谷歌展开过许多创新研发项目,这些项目曾经引发了舆论关注和科技发烧友的兴趣,但是项目均以失败告终,比如超级光纤宽带服务.谷歌眼镜.机器人研究等.后来在成立控股母公司Alphab ...

  6. 前端面试送命题(一)-JS三座大山

    前言 本篇文章比较适合3年以上的前端工作者,JS三座大山分别指:原型与原型链,作用域及闭包,异步和单线程. 原型与原型链 说到原型,就不得不提一下构造函数,首先我们看下面一个简单的例子: functi ...

  7. 前端面试送命题-JS三座大山

    前言 本篇文章比较适合3年以上的前端工作者,JS三座大山分别指:原型与原型链,作用域及闭包,异步和单线程. 原型与原型链 说到原型,就不得不提一下构造函数,首先我们看下面一个简单的例子: functi ...

  8. 有一座山就像一个笔架子

    我胆子小的天天 今天的我胆子小的天天,,我的天天脚像绑上了重重的沙袋,来了,它舌头一伸,有一座山就像一个笔架子,我发现平日里疾走如飞的汽车,坚持不了了,飞蛾就不见了,好浓的雾啊. 脸色苍白,第三个来回 ...

  9. 向面前的第一座山进军!!!

    人活着要同事经历这两种境界,一份是现实的生活,一份是不及的灵魂.常常地,面对着纷纭杂沓的 世界,我们就这样或那样地迷惑了:不再艰辛山那边由迷人的风景.然而在闪的这一边滞步是,我们却没 有更好的理由让自 ...

最新文章

  1. angularjs解决方案之 递归模板
  2. MySQL InnoDB Cluster安装
  3. linux 中断奶乱码,科学断奶经历:早中晚三次母乳,一个月内循序渐进自然断奶...
  4. lucene之Field属性的解释
  5. tls jdk_使用JDK 13查看TLS配置
  6. C# 使用PictureBox控件--点击切换图片
  7. 这些年,我们无法忘却的jQuery日历插件
  8. table 条数过大优化_MySQL数据库优化的介绍(图文)
  9. 洛谷 U5737 纸条
  10. VScode 配置 Java 环境
  11. Java常用Linux命令集合
  12. 使用modscan32工具踩的坑
  13. bootstrap 半透明背景_【小技巧】微信 QQ 半透明主题壁纸设置方法
  14. Gluster 常用操作命令
  15. android 3d成像,android camera 实现3d效果
  16. 音调识别 php,PHP 汉字转拼音工具
  17. Ubuntu桌面系统卡住
  18. big mac sur 免驱显卡_黑苹果免驱显卡速查表
  19. Blob detection+高斯二阶导(拉普拉斯模板)+尺度的选择+加快速度(DoG+分层处理)
  20. Java设计模式----------享元模式

热门文章

  1. 【原创】php ssh2 远程秘钥登录华三防火墙F5030
  2. java if经典程序_java经典程序题15道(另附自己做的答案)
  3. 第四周项目3--单链表应用之递增判断
  4. 试用期三个月,快转正的时候,领导说,“你的表现没有达到预期”
  5. PHP使用文字检索,关键词查询,分词查询
  6. 网络监控cacti1.2.12邮件报警(三)
  7. 美团App 插件化实践
  8. 美团外卖订单小票打印规范
  9. v-on:click获取当前事件对象元素
  10. VMM物理内存、虚拟内存管理,偷页算法