今天是日常工作的一天,在我吃着下午茶看我的小伙伴写的代码时,我发现一段很有意思的代码

async function preLoad() {
    const saveFileList = await Promise.all(afxList.map(item =>
      this.$api.downloadFile({url: item, header: {Range: 'bytes=0-'}})
      .then(({tempFilePath}) => this.$api.saveFile({tempFilePath}))
      .then(({savedFilePath}) => Promise.resolve(savedFilePath))
  ));
  this.$api.setStorageSync('AFX_SOURCE', saveFileList);
}

我第一眼看到这段代码时,我是有点懵逼的,我没有反应过来我的小伙伴为什么要这样写,你可能会说:这段代码并不难,但是我们看不懂API啊,好,那我把这段代码概念化一下

async function async1() {}
async function async2() {}
async function fun1() {
    const a = await Promise.all(async1()
    .then((value1) => async2(value1))
    .then((value2) => Promise.resolve(value2)));
    console.log(a);
}

好的,现在你没有理由看不懂这一段代码了吧,你可能又会说这不就是一段普通的Promise的代码吗?有什么特别的呢?好,那我就来给你分析一下,这段代码包含的功力。

首先我想说的是,也许我我们在日常开发中对async和await的使用已经麻木,但是如果我们不知道他的原理,那很可能就会在这里出现执行顺序错误的问题,所以这里的知识点非常重要!
如果你看不懂这段代码的执行逻辑,或者不懂Promise.all,或者不懂Promise的链式调用,那我希望你能先去学会Promise,不然接下来的内容会让你十分不开心。

你会不会有这样的疑问:Promise.resolve(value2)这里明明触发了一个成功的回调函数,这个成功的回调函数我们并没有注册啊,他为什么可以被保存在a这个变量里?await这个关键字到底干了什么?await后面的代码并不是每一次都会有Promise.resolve()这个东西,通常情况下,await后面会跟一个请求,为什么要用await呢?直接请求的结果为什么不能赋给变量呢?

想要解决这些问题,我们首先要知道await后面的请求是异步的,如果我们不对异步的请求做处理,只接把请求赋给一个变量,那么这个变量是没有异步请求的结果的,所以我们才用到了async和await,async的作用是使该函数返回一个Promise,await的作用是让后面的异步请求同步执行,然后使该请求的结果作为成功回调的参数,而await后面的代码就相当于是成功的回调函数,这几句话也许有点抽象,我们用代码来描述这句话

// 我们把fun1这个函数Promise化
function fun1() {
    return new Promise(() => {
    Promise.all(async1()
      .then((value1) => async2(value1))
      .then((value2) => Promise.resolve(value2)))
    .then((a) => { // 执行完Promise.all后的成功函数的回调
      console.log(a) 
    });
  })
}

结合代码和上面的论述,一切就清晰起来了,这就是async和await的原理,我们要死死记住,await就是让异步请求同步执行,await后面的代码相当于是成功回调函数中的代码,而该函数的参数就是异步请求的结果。
到这里你可能会说,好的,我懂了,真的是这样吗?我刚开始就说了,如果这里你理解不清楚,那么很容易出现执行顺序上的问题,我们来做道题吧!

async function async1() {
    console.log('async1 start');
  await async2();
  console.log('async1 end');
}
async function async2() {
    console.log('async2');
}
console.log('script start');
setTimeout(() => {
    console.log('setTimeout');
}, 0);
async1();
new Promise((resolve) => {
    console.log('promise 1');
  resolve();
}).then(() => {
    console.log('promise 2');
})
console.log('scrpit end');

请输出这段代码的执行结果
这道题中牵扯的不仅仅是async的知识,还有js事件循环机制的知识,因为这不是本章的主要内容,所以我默认你是会js事件循环的

遇到async和await关键字,我们都可以把他转换成Promise,也就是降到ES6,相信各位对ES6代码进行事件循环分析已经很熟练了,其实是一样的道理,我们只需要牢记async和await的原理,await前的代码同步执行,await的请求也是同步执行,await后面的代码是成功回调,会被放入微队列中等待执行。

按照这样的思路分析,我们可以得到正确答案
script start async1 start async2 promise1
script end async1 end promise2 setTimeout
你做对了吗?学会了吗?