原贴链接:javascript异步总结归档
这是javascript异步系列文章的第十篇,也是最后一篇,总结并归档。
十篇文章不足以把异步解释清清楚楚,
异步这块知识点在js中占比很大,很多莫名其妙的bug也出现在这里,
比如说下面的这个栗子:
一个bug
重点看郭靖
let Hero = {gj: "初始化郭靖",hr: {name: "黄蓉",sex: "女"}};function demo(obj) {return new Promise((resolve, reject) => {resolve("赋值郭靖");}).then(res => {obj.gj = res;console.log("then-Hero.gj:", Hero.gj);});}demo(Hero);console.log("Hero:", Hero);console.log("Hero.gj:", Hero.gj);
会输出什么?

重点看郭靖
先输出Hero,在输出Hero.gj
梳理一下代码执行流程
- 先执行demo(Hero);
- 进入Promise, resolve(“赋值郭靖”);
- “赋值郭靖”作为Promise的返回值
- then 是异步,进入异步队列
- 继续走同步的主线程,console.log(“Hero:”, Hero);Hero没有进行赋值操作,所以Hero值没变
- 继续走同步的主线程,console.log(“Hero.gj:”, Hero.gj);Hero没有进行赋值操作,所以Hero.gj值依然没变=>初始化郭靖
- 同步执行完毕,开始执行事件循环中的异步事件=>then,then 方法中对Hero中的gj进行赋值,
- 所以 console.log(“then-Hero.gj:”, Hero.gj);//=>赋值郭靖
我相信你一定遇到过类似的bug,这是哪里出了问题?
首先,肯定是异步出了问题,但是这个不只是异步的问题
引用类型
再来一个栗子
let Hero = {let gj = "初始化郭靖";function demo1(str) {return new Promise((resolve, reject) => {resolve("赋值郭靖");}).then(res => {gj = res;console.log('then-gj:',gj);});}demo1(gj);console.log("gj:", gj);
看下输出:
//=>gj: 初始化郭靖//=>then-gj: 赋值郭靖
如果这看不出是引用类型的问题,可以试试这个
console.log("Hero:", Hero);console.log("str-Hero:", JSON.stringify(Hero));
看下输出:

Hero是引用类型,如果单单只是引用类型,也没什么,可是引用类型遇到了console.log,就出问题了
console.log
看一个小栗子
const json = { a: 1, b: 2 };console.log(json);json.a = 3;
看下输出:

看到这个输出是不是很开心?当你把这个json展开的时候,
惊喜出现了
console.log在打印应用类型的时候,可能会不太靠谱
最上面的栗子,我们使用debugger试一下
//省略demo(Hero);console.log("Hero:", Hero);debuggerconsole.log("Hero.gj:", Hero.gj);
这次再看下输出:

现在看来,似乎全是console.log的问题,和引用类型和异步没关系
解决方案
这个bug的出现的根源是异步出现了问题
修改下代码(具体根据自己的需求进行修改,如下代码仅供参考)
let Hero = {gj: "初始化郭靖",hr: {name: "黄蓉",sex: "女"}};function demo(obj) {return new Promise((resolve, reject) => {resolve("赋值郭靖");}).then(res => {obj.gj = res;console.log("then-Hero.gj:", Hero.gj);}).then(() => {console.log("Hero:", Hero);console.log("Hero.gj:", Hero.gj);});}demo(Hero);
输出:

关于异步有一篇深入的介绍
深入核心,详解事件循环机制
再说一下引用类型
在vue项目中会有一些规则,例如
- 子组件不能修改父组件的值
- state只能在mutation中修改
至于为什么这么要求,我们不在这里讨论,我们要说的是,如果你不这么做,vue就会抛出警告
但是引用类型除外,
如果一个变量是引用类型,在子组件中修改,vue不抛出警告
如果state是个引用类型,在mutation外部修改,vue不抛出警告
但是最好不要这么做,否则日后很难定位bug根源
异步
我们说起异步,之前首先想到的是回调函数,但是回调函数存在各种问题, ES6中出现了Promise,解决了回调函数问题,Promise中我们又介绍了几个常用的API
Promise.all()、Promise.race()、Promise.finally()、Promise.then()、Promise.catch()
我们后来又介绍了Generator,通过Generator引出了async await,
async就是Generator的语法糖,简化了Generator的使用方法
async无法取代Promise,async的使用依赖于Promise
有人说async await是处理异步的终极方案,这个说法有些极端
处理异步的方法我们介绍很多,没有最好的,只有最合适的
会的多了,选择也就多了,代码质量自然就会高
所以,这几种异步的处理方式我们都要学会
关于JS异步就介绍到这里
END
