原贴链接: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);
debugger
console.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