深度剖析async和await
今天是日常工作的一天,在我吃着下午茶看我的小伙伴写的代码时,我发现一段很有意思的代码
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
你做对了吗?学会了吗?