陌上寒

陌上寒个人博客

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);

会输出什么?
《javascript异步总结归档》
重点看郭靖
先输出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));

看下输出:
《javascript异步总结归档》
Hero是引用类型,如果单单只是引用类型,也没什么,可是引用类型遇到了console.log,就出问题了

console.log

看一个小栗子

const json = { a: 1, b: 2 };
console.log(json);
json.a = 3;

看下输出:
《javascript异步总结归档》
看到这个输出是不是很开心?当你把这个json展开的时候,
惊喜出现了
《javascript异步总结归档》
console.log在打印应用类型的时候,可能会不太靠谱
最上面的栗子,我们使用debugger试一下

//省略
demo(Hero);
console.log("Hero:", Hero);
debugger
console.log("Hero.gj:", Hero.gj);

这次再看下输出:
《javascript异步总结归档》
现在看来,似乎全是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);

输出:
《javascript异步总结归档》
关于异步有一篇深入的介绍
深入核心,详解事件循环机制

再说一下引用类型

在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

发表评论

电子邮件地址不会被公开。 必填项已用*标注