JS三座大山再学习 ---- 异步和单线程
前言
写这一篇的时候,西瓜君查阅了很多资料和文章,但是相当多的文章写的都很简单,甚至互相之间有矛盾,这让我很困扰;同时也让我坚定了要写出一篇好的关于JS异步、单线程、事件循环的文章,下面,让我们一起来学习本文吧,冲鸭~~
单线程
### 1. 什么是单线程
//栗子1
console.log(1)
console.log(2)
console.log(3)
//输出顺序 1 2 3
单线程即同一时间只做一件事
2. JavaScript为什么是单线程
- 首先是历史原因,在创建 javascript 这门语言时,多进程多线程的架构并不流行,硬件支持并不好。
- 其次是因为多线程的复杂性,多线程操作需要加锁,编码的复杂性会增高。
- 而且,如果同时操作 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.执行栈与任务队列
先理解几个概念
- JS任务 分为同步任务(synchronous)和异步任务(asynchronous)
- 同步任务都在 JS引擎线程(主线程) 上执行,形成一个执行栈(call stack)
- 事件触发线程 管理一个 任务队列(Task Queue)
- 异步任务 触发条件达成,将 回调事件 放到任务队列(Task Queue)中
- 执行栈中所有同步任务执行完毕,此时JS引擎线程空闲,系统会读取任务队列,将可运行的异步任务回调事件添加到执行栈中,开始执行
当一个JS文件第一次执行的时候,js引擎会 解析这段代码,并将其中的同步代码 按照执行顺序加入执行栈中,然后从头开始执行。如果当前执行的是一个方法,那么js会向执行栈中添加这个方法的执行环境,然后进入这个执行环境继续执行其中的代码。当这个执行环境中的代码 执行完毕并返回结果后,js会退出这个执行环境并把这个执行环境销毁,回到上一个方法的执行环境。这个过程反复进行,直到执行栈中的代码全部执行完毕。
举个栗子:
//Event loop
//(1)
console.log(1)
//(2)
setTimeout(()=>{
console.log(2)
},100)
//(3)
console.log(3)
- 先解析整段代码,按照顺序加入到执行栈中,从头开始执行
- 先执行(1),是同步的,所以直接打印 1
- 执行(2),发现是 setTimeout,于是调用浏览器的方法(webApi)执行,在 100ms后将 console.log(2) 加入到任务队列
- 执行(3),同步的,直接打印 3
- 执行栈已经清空了,现在检查任务队列,(执行太快的话可能此时任务队列还是空的,没到100ms,还没有将(2)的打印加到任务队列,于是不停的检测,直到队列中有任务),发现有 console.log(2),于是添加到执行栈,执行console.log(2),同步代码,直接打印 2 (如果这里是异步任务,同样会再走一遍循环:-->任务队列->执行栈)
所以结果是 1 3 2
注:setTimeout/Promise等我们称之为任务源。而进入任务队列的是他们指定的回调
2.宏任务(macro task)与微任务(micro task)
上面的循环只是一个宏观的表述,实际上异步任务之间也是有不同的,分为 宏任务(macro task) 与 微任务(micro task),最新的标准中,他们被称为 task与 jobs
- 宏任务有哪些:script(整体代码), setTimeout, setInterval, setImmediate, I/O, UI rendering(渲染)
- 微任务有哪些:process.nextTick, Promise, Object.observe(已废弃), MutationObserver(html5新特性)
下面我们再详细讲解一下执行过程
执行栈在执行的时候,会把宏任务放在一个宏任务的任务队列,把微任务放在一个微任务的任务队列,在当前执行栈为空的时候,主线程会 查看微任务队列是否有事件存在。如果微任务队列不存在,那么会去宏任务队列中 取出一个任务 加入当前执行栈;如果微任务队列存在,则会依次执行微任务队列中的所有任务,直到微任务队列为空(同样,是吧队列中的事件加到执行栈执行),然后去宏任务队列中取出最前面的一个事件加入当前执行栈...如此反复,进入循环。
注:
- 宏任务和微任务的任务队列都可以有多个
- 当前执行栈执行完毕时会立刻先处理所有微任务队列中的事件,然后再去宏任务队列中取出一个事件。同一次事件循环中,微任务永远在宏任务之前执行。
- 不同的运行环境 循环策略可能有不同,这里探讨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),宏任务,调用webapi setTimeout,这个方法会在100ms后将回调函数放入宏任务的任务队列
- 执行(2),同(1),但是会比(1)稍后一点
- 执行(3),同步执行new Promise,然后执行(4),直接打印 3 ,然后resolve(4),然后.then(),把(5)放入微任务的任务队列
- 执行(6),同上,先打印 5 ,再执行resolve(6),然后.then()里面的内容(8)加入到微任务的任务队列
- 执行(9),同步代码,直接打印 7
- 执行(10),同(1)和(2),只是时间更短,会在 50ms 后将回调 console.log(8) 加入宏任务的任务队列
- 现在执行栈清空了,开始检查微任务队列,发现(5),加入到执行栈执行,是同步代码,直接打印 4
- 任务队列又执行完了,又检查微任务队列,发现(8),打印 6
- 任务队列又执行完了,检查微任务队列,没有任务,再检查宏任务队列,此时如果超过了50ms的话,会发现 console.log(8) 在宏任务队列中,于是执行 打印 8
- 依次打印 1 2
注:因为渲染也是宏任务,需要在一次执行栈执行完后才会执行渲染,所以如果执行栈中同时有几个同步的改变同一个样式的代码,在渲染时只会渲染最后一个
结语
写到这里,仍然觉得还有很多知识点没有写出来,但是想写又不知道从哪里入手。于是决定今天就写到这里,日后再做补充。
到这篇,JS三座大山系列就暂时完结了,在这其中自己也学到了很多,希望能继续输出一些有意义的东西,加油,西瓜君~~
参考文章:
https://www.jianshu.com/p/12b9f73c5a4f
https://www.cnblogs.com/cangqinglang/p/8967268.html
如有错误,请斧正
以上
出处:https://www.cnblogs.com/bloglixin/p/11959219.html
JS三座大山再学习 ---- 异步和单线程的更多相关文章
- JS三座大山再学习(三、异步和单线程)
本文已发布在西瓜君的个人博客,原文传送门 前言 写这一篇的时候,西瓜君查阅了很多资料和文章,但是相当多的文章写的都很简单,甚至互相之间有矛盾,这让我很困扰:同时也让我坚定了要写出一篇好的关于JS异步. ...
- JS三座大山再学习(一、原型和原型链)
原文地址 ## 前言 西瓜君之前学习了JS的基础知识与三座大山,但之后工作中没怎么用,印象不太深刻,这次打算再重学一下,打牢基础.冲鸭~~ 原型模式 JS实现继承的方式是通过原型和原型链实现的,JS中 ...
- JS三座大山再学习 ---- 原型和原型链
本文已发布在西瓜君的个人博客,原文传送门 ## 前言 西瓜君之前学习了JS的基础知识与三座大山,但之后工作中没怎么用,印象不太深刻,这次打算再重学一下,打牢基础.冲鸭~~ 原型模式 JS实现继承的方式 ...
- JS三座大山再学习(二、作用域和闭包)
原文地址 作用域 JS中有两种作用域:全局作用域|局部作用域 栗子1 console.log(name); //undefined var name = '波妞'; var like = '宗介' c ...
- JS三座大山再学习 ---- 作用域和闭包
本文已发布在西瓜君的个人博客,原文传送门 作用域 JS中有两种作用域:全局作用域|局部作用域 栗子1 console.log(name); //undefined var name = '波妞'; v ...
- 面试 09-02.js运行机制:异步和单线程
09-02.js运行机制:异步和单线程 #前言 面试时,关于同步和异步,可能会问以下问题: 同步和异步的区别是什么?分别举一个同步和异步的例子 一个关于 setTimeout 的笔试题 前端使用异步的 ...
- js再学习笔记
#js再学习笔记 ##基本 1.js严格区分大小写 2.js末尾的分号可加,也可不加 3.六种数据类型(使用typeof来检验数据的类型) `typeof` - undefined: `var ...
- JS(异步与单线程)
JS(异步与单线程) 题目1.同步和异步的区别是什么,试举例(例子见知识点) 区别: 1.同步会阻塞代码执行,而异步不会 2.alert 是同步,setTimeout 是异步 题目2.关于 setTi ...
- 【原创】分布式之数据库和缓存双写一致性方案解析(三) 前端面试送命题(二)-callback,promise,generator,async-await JS的进阶技巧 前端面试送命题(一)-JS三座大山 Nodejs的运行原理-科普篇 优化设计提高sql类数据库的性能 简单理解token机制
[原创]分布式之数据库和缓存双写一致性方案解析(三) 正文 博主本来觉得,<分布式之数据库和缓存双写一致性方案解析>,一文已经十分清晰.然而这一两天,有人在微信上私聊我,觉得应该要采用 ...
随机推荐
- idea插件(mybatis框架下mapper接口快速跳转对应xml文件)亲测好用!
我相信目前在绝大部分公司里,主要使用的框架是S(spring)S(spring MVC)M(mybatis),其中mybatis总体架构是编写mapper接口,框架扫描其对应的mapper.xml文件 ...
- centos7虚拟机端口命令
cat /etc/redhat-release # 查看centos 版本 Centos7端口常见命令 虚拟机新开了5005端口,系统内部是显示开了的,但是外部不能访问端口. 一些需要用到的命令: ...
- getpatch
import time import os import math import sys import os,os.path,shutil import numpy as np import cv2 ...
- Java集合源码分析之ArrayList(JDK1.8)
package annoction; import java.util.*; import java.util.function.Consumer; import java.util.function ...
- 【day07】php
一.数组(Array) 1.数组:一组数据的集合 2.数组的分类: 索引数组:键名称是整数,编号从0开始 关联数组:键名称是字符串 3.定义一维数组 ...
- 第04组 Alpha事后诸葛亮
一.组长博客:地址 二.Postmortem模板 设想和目标 1.我们的软件要解决什么问题?是否定义得很清楚?是否对典型用户和典型场景有清晰的描述? 我们要解决的问题是让大学生可以通过福鱼网站将暂时无 ...
- Flutter基础系列之入门(一)
1.Flutter是什么? 官方介绍:Flutter是谷歌的移动UI框架,可以快速在iOS和Android上构建高质量的原生用户界面. Flutter可以与现有的代码一起工作.在全世界,Flutter ...
- vue做的项目每次打开新页面不会显示页面顶部的解决办法
在main.js 中添加代码: router.afterEach((to,from, next) => { window.scrollTo(0,0) }) 然后就会发现每次打开页面都是显示的是页 ...
- SimdJsonSharp:每秒解析千兆字节的JSON
SimdJsonSharp: Parsing gigabytes of JSON per second C# version of lemire/simdjson (by Daniel Lemire ...
- Nginx自建SSL证书部署HTTPS网站
一.创建SSL相关证书 1.安装Nginx(这里为了测试使用yum安装,实际看具体情况) [root@localhost ~]# yum install nginx -y #默认yum安装已经支持SS ...