function takeLongTime(){ return new Promise((resolve) => { setTimeout(() => resolve('long time value'),1000); }) } async function test(){ let v = await takeLongTime();//等待异步操作的结果,阻塞后面代码的执行 console.log(v); }
眼尖的同学已经发现 takeLongTime() 没有申明为 async。实际上,takeLongTime() 本身就是返回的 Promise 对象,加不加 async结果都一样,如果没明白,请回过头再去看看上面的“async 起什么作用”。
又一个疑问产生了,这两段代码,两种方式对异步调用的处理(实际就是对 Promise 对象的处理)差别并不明显,甚至使用 async/await 还需要多写一些代码,那它的优势到底在哪?
async/await 的优势在于处理 then 链
单一的 Promise 链并不能发现 async/await 的优势,但是,如果需要处理由多个 Promise 组成的 then 链的时候,优势就能体现出来了(很有意思,Promise 通过 then 链来解决多层回调的问题,现在又用 async/await 来进一步优化它)。
假设一个业务,分多个步骤完成,每个步骤都是异步的,而且依赖于上一个步骤的结果。我们仍然用 setTimeout 来模拟异步操作:
/* * 传入参数n,表示这个函数执行的时间(毫秒) * 执行的结果是 n+200,这个值将用于下一步骤 */ function takeLongTime(n){ return new Promise((resolve) => { setTimeout(() => resolve(n + 200),n); }) } function step1(n){ console.log(`step1 with ${n}`); return takeLongTime(n); } function step2(n){ console.log(`step2 with ${n}`); return takeLongTime(n); } function step3(n){ console.log(`step3 with ${n}`); return takeLongTime(n); }
现在用 Promise 方式来实现这三个步骤的处理。
function doIt(){ console.time('doIt'); let time1 = 300; step1(time1) .then((time2) => step2(time2)) .then((time3) => step3(time3)) .then((result) => { console.log(`result is ${result}`); console.timeEnd("doIt"); }) } doIt(); //执行结果为: //step1 with 300 //step2 with 500 //step3 with 700 //result is 900 //doIt: 1510.2490234375ms
输出结果 result 是 step3() 的参数 700 + 200 = 900。doIt() 顺序执行了三个步骤,一共用了 300 + 500 + 700 = 1500 毫秒,和 console.time()/console.timeEnd() 计算的结果一致。
如果用 async/await 来实现呢,会是这样。
async function doIt() { console.time('doIt'); let time1 = 300; let time2 = await step1(time1);//将Promise对象resolve(n+200)的值赋给time2 let time3 = await step1(time2); let result = await step1(time3); console.log(`result is ${result}`); console.timeEnd('doIt'); } doIt(); //执行结果为: //step1 with 300 //step2 with 500 //step3 with 700 //result is 900 //doIt: 1512.904296875ms
结果和之前的 Promise 实现是一样的,但是这个代码看起来是不是清晰得多,几乎跟同步代码一样。
还有更酷的
现在把业务要求改一下,仍然是三个步骤,但每一个步骤都需要之前每个步骤的结果。
/* * 传入参数n,表示这个函数执行的时间(毫秒) * 执行的结果是 n+200,这个值将用于下一步骤 */ function takeLongTime(n){ return new Promise((resolve) => { setTimeout(() => resolve(n + 200),n); }) } function step1(n){ console.log(`step1 with ${n}`); return takeLongTime(n); } function step2(m,n){ console.log(`step2 with ${m} + ${n}`); return takeLongTime(m + n); } function step3(k,m,n){ console.log(`step3 with ${k} + ${m} + ${n}`); return takeLongTime(k + m + n); }
这回先用 async/await 来写:
async function doIt() { console.time('doIt'); let time1 = 300; let time2 = await step1(time1);//将Promise对象resolve(n+200)的值赋给time2 let time3 = await step2(time2,time1); let result = await step3(time3,time2,time1); console.log(`result is ${result}`); console.timeEnd('doIt'); } doIt(); //执行结果为: //step1 with 300 //step2 with 500 + 300 //step3 with 1000 + 500 + 300 //result is 2000 //doIt: 2916.655029296875ms
除了觉得执行时间变长了之外,似乎和之前的示例没啥区别啊!别急,认真想想如果把它写成 Promise 方式实现会是什么样子?
function doIt() { console.time('doIt'); let time1 = 300; step1(time1) .then((time2) => { return step2(time1,time2) .then((time3) => [time1,time2,time3])//step3需要用到time1,time2,time3,因此需要返回 }) .then((times) => { let [time1,time2,time3] = times; return step3(time1,time2,time3) }) .then((result) => { console.log(`result is ${result}`); console.timeEnd('doIt'); }) } doIt(); //执行结果为: //step1 with 300 //step2 with 300 + 500 //step3 with 300 + 500 + 1000 //result is 2000 //doIt: 2919.49609375ms
有没有感觉有点复杂的样子?那一堆参数处理,就是 Promise 方案的死穴—— 参数传递太麻烦了,看着就晕!
注意点
就目前来说,已经理解 async/await 了吧?但其实还有一些事情没提及——Promise 有可能 reject 啊,怎么处理呢?
await 命令后面的 Promise 对象,运行结果可能是 rejected,所以最好把 await 命令放在 try...catch 代码块中。