参考博客:
https://segmentfault.com/a/1190000007535316
https://www.runoob.com/w3cnote/es6-async.html
一、async
带async关键字的函数,是声明异步函数,返回值是promise对象,如果async关键字函数返回的不是promise,会自动用Promise.resolve()包装。
因此 async 函数总会返回一个 Promise 对象,可以使用 then 方法添加回调函数。
async function hello() { return 'hello' } var res = hello(); console.log(res); // Promise {<fulfilled>: "hello"} res.then(v =>{ console.log(v); // hello })
所以,async 函数返回的是一个 Promise 对象,如果在函数中直接 return 一个值,async 会把这个值通过 Promise.resolve() 封装成 Promise 对象。
如果 async 函数没有返回值,它会返回 Promise.resolve(undefined)。
async 函数中可能会有 await 表达式,async 函数执行时,如果遇到 await 就会先暂停执行 ,等到触发的异步操作完成后,恢复 async 函数的执行并返回解析值。
await 关键字仅在 async function 中有效。如果在 async function 函数体外使用 await ,你只会得到一个语法错误。
二、await
await 操作符用于等待一个 Promise 对象, 它只能在异步函数 async function 内部使用。
await等待右侧表达式的结果,这个结果是promise对象或者其他值。
await针对所跟不同表达式的处理方式:
- Promise 对象:await 会暂停执行,等待 Promise 对象 resolve,然后得到 resolve 的值(即已经解析出Promise的值了,此时不再是Promise对象),作为 await 表达式的运算结果,然后恢复 async 函数的执行并返回解析值。
- 非 Promise 对象:直接返回对应的值。
// 返回 Promise 对象的处理结果。如果等待的不是 Promise 对象,则返回该值本身。 // 如果一个 Promise 被传递给一个 await 操作符,await 将等待 Promise 正常处理完成并返回其处理结果。 function testAwait (x) { return new Promise(resolve => { setTimeout(() => { resolve(x); }, 2000); }); } async function helloAsync() { var x = await testAwait ("hello world"); console.log(x); } helloAsync(); // hello world
正常情况下,await 命令后面是一个 Promise 对象,它也可以跟其他值,如字符串,布尔值,数值以及普通函数。
function testAwait(){ console.log("testAwait"); } async function helloAsync(){ await testAwait(); console.log("helloAsync"); } helloAsync(); // testAwait // helloAsync
三、async/await的优势
async/await 的优势在于处理 then 链。
如果一个功能需要多个步骤(函数)协同工作,这时async/await 的优势就出来了。
单一的 Promise 链并不能发现 async/await 的优势,但是,如果需要处理由多个 Promise 组成的 then 链的时候,优势就能体现出来了。
// 我需要从taskInfo中一步一步的拿到username的值,那么函数如下 function taskInfo() { return new Promise(resolve => { var person = { "result": { "data": {"username": "zzz"} } } resolve(person); }); } function step1() { var data = taskInfo(); return data; } function step2(data) { return data.result; } function step3(data) { return data.data; } function step4(data) { return data.username; } // 用 Promise 的链式方式来实现 function main() { step1() .then(res1 => step2(res1)) .then(res2 => step3(res2)) .then(res3 => step4(res3)) .then(response => { console.log(response); }); } main(); // zzz // 用 async/await 来实现 async function main() { var res1 = await step1(); var res2 = await step2(res1); var res3 = await step3(res2); var response = await step4(res3); console.log(response); } main(); // zzz
结果和之前的 Promise 实现是一样的,但是这个代码清晰得多,几乎跟同步代码一样。
// 业务修改一下 function step1(n) { console.log(`step1 with ${n}`); return takeLongTime(n); } function step2(m, n) { console.log(`step2 with ${m} and ${n}`); return takeLongTime(m + n); } function step3(k, m, n) { console.log(`step3 with ${k}, ${m} and ${n}`); return takeLongTime(k + m + n); } // 先用 async/await 来写 async function doIt() { console.time("doIt"); const time1 = 300; const time2 = await step1(time1); const time3 = await step2(time1, time2); const result = await step3(time1, time2, time3); console.log(`result is ${result}`); console.timeEnd("doIt"); } doIt(); // 似乎和之前的示例没啥区别 // 那么写成 Promise 方式对比 function doIt() { console.time("doIt"); const time1 = 300; step1(time1) .then(time2 => { return step2(time1, time2) .then(time3 => [time1, time2, time3]); }) .then(times => { const [time1, time2, time3] = times; return step3(time1, time2, time3); }) .then(result => { console.log(`result is ${result}`); console.timeEnd("doIt"); }); } doIt(); // 一堆参数处理,就是 Promise 方案的死穴—— 参数传递太麻烦了
四、promise + async/await
Promise 回调函数中里面 无法把里面值通过 return 返回出去,只能由 resolve()函数把里面的值传递到then的函数中。
如果只是单纯想使用这个值,那么得到then函数中获取,例如: promiseObj.then(res => {var result = res;}),这样才把数据赋值到result中使用,
而且如果需要对这个值进行多次处理,那么回调函数可能会出现多层嵌套的问题,原因就是promise对象中的值无法弄出来,只能不断的嵌套then使用。
但是 promise + async/await 解决了这个问题,从此js没有这个问题。
需要注意的是,使用await取出来的值,已经不是Promise对象了,没有then catch方法。
<!DOCTYPE html> <html lang="zh-CN"> <head> </head> <body> </body> <script> // 示例 loadData(); async function loadData() { // await 能够拿到Promise对象的值 var dataJson = await getPromise(); // 此时dataJson就是Object对象,不是Promise对象了 console.log("await后获取到的data: "+JSON.stringify(dataJson)) // {"name":"zz"} console.log("dataJson.name: "+dataJson.name) // zz } // 用函数构造一个Promise对象 function getPromise() { return new Promise(function (resolve, reject) { var data = {"name": "zz"} console.log("原始data: "+ JSON.stringify(data)); // {"name":"zz"} resolve(data); }); } var p = new Promise(function (resolve, reject) { var xx = {"result": [1,2,3,4]} resolve(xx); }); // 这里的p没有使用await,仍是Promise对象,可用then p.then(val => { var arr = val.result; console.log("arr: "+JSON.stringify(arr)); // [1,2,3,4] }); </script> </html>