async 与 await 解决了什么问题 在 async/await 之前,我们有三种方式写异步代码
嵌套回调
以 Promise 为主的链式回调
使用 Generators
async/await 特点
async/await 更加语义化,async 是“异步”的简写,async function 用于申明一个 function 是异步的; await,可以认为是 async wait 的简写, 用于等待一个异步方法执行完成;
async/await 是一个用同步思维解决异步问题的方案(等结果出来之后,代码才会继续往下执行)
可以通过多层 async function 的同步写法代替传统的 callback 嵌套
async 定义的函数的返回值都是 promise,await 后面的函数会先执行一遍,然后就会跳出整个 async 函数来执行后面 js 栈的代码
async function 语法
自动将常规函数转换成 Promise,返回值也是一个 Promise 对象
只有 async 函数内部的异步操作执行完,才会执行 then 方法指定的回调函数
异步函数内部可以使用 await
1 async function name ([param[, param[, ... param]]] ) { statements }
await 语法
await 放置在 Promise 调用之前,await 强制后面点代码等待,直到 Promise 对象 resolve,得到 resolve 的值作为 await 表达式的运算结果
await 只能在 async 函数内部使用,用在普通函数里就会报错
1 [return_value] = await expression;
expression: 一个 Promise 对象或者任何要等待的值。
返回值:返回 Promise 对象的处理结果。如果等待的不是 Promise 对象,则返回该值本身。
实例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 function pm ( ) { return new Promise ((resolve, reject ) => { resolve ("promise value:1" ); }); }async function test ( ) { let a = await pm (); let b = await "not promise value:2" ; console .log (a); console .log (b); return a + b; }test ();
错误处理 在 async 函数里,无论是 Promise reject 的数据还是逻辑报错,都会被默默吞掉,所以最好把 await 放入 try{}catch{}中,catch 能够捕捉到 Promise 对象 rejected 的数据或者抛出的异常
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 function timeout (ms ) { return new Promise ((resolve, reject ) => { setTimeout (() => { reject ("error" ); }, ms); }); }async function asyncPrint (ms ) { try { console .log ("start" ); await timeout (ms); console .log ("end" ); } catch (err) { console .log (err); } }asyncPrint (1000 );
如果不用 try/catch 的话,也可以像下面这样处理错误(因为 async 函数执行后返回一个 promise)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 function timeout (ms ) { return new Promise ((resolve, reject ) => { setTimeout (() => {reject ('error' )}, ms); }); }async function asyncPrint (ms ) { console .log ('start' ); await timeout (ms) console .log ('end' ); }asyncPrint (1000 ).catch (err => { console .log (err); });
如果你不想让错误中断后面代码的执行,可以提前截留住错误,像下面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 function timeout (ms ) { return new Promise ((resolve, reject ) => { setTimeout (() => { reject ("error" ); }, ms); }); }async function asyncPrint (ms ) { console .log ("start" ); await timeout (ms).catch ((err ) => { console .log (err); }); console .log ("end" ); }asyncPrint (1000 );
使用场景 多个 await 命令的异步操作,如果不存在依赖关系(后面的 await 不依赖前一个 await 返回的结果),用 Promise.all()让它们同时触发
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 function test1 ( ) { return new Promise ((resolve, reject ) => { setTimeout (() => { resolve (1 ); }, 1000 ); }); }function test2 ( ) { return new Promise ((resolve, reject ) => { setTimeout (() => { resolve (2 ); }, 2000 ); }); }async function exc1 ( ) { console .log ("exc1 start:" , Date .now ()); let res1 = await test1 (); let res2 = await test2 (); console .log ("exc1 end:" , Date .now ()); }async function exc2 ( ) { console .log ("exc2 start:" , Date .now ()); let [res1, res2] = await Promise .all ([test1 (), test2 ()]); console .log ("exc2 end:" , Date .now ()); }exc1 ();exc2 ();
exc1 的两个并列 await 的写法,比较耗时,只有 test1 执行完了才会执行 test2
比较发现 exc2 的用 Promise.all 执行更快一些
项目中使用
通过 babel 来使用。
只需要设置 presets 为 stage-3 即可。
安装依赖 1 npm install babel-preset-es2015 babel-preset-stage-3 babel-runtime babel-plugin-transform-runtime
修改.babelrc 1 2 "presets" : ["es2015" , "stage-3" ],"plugins" : ["transform-runtime" ]