基础系列-async&await
async 函数与 await 命令的使用
解决的问题:
减少多层嵌套,可以进行同步的写法。
使Promise
链式调用可控,即中间promise
出现问题后能够跳出继续向下调用,能够及时处理错误。
优点:
(1)内置执行器。async
函数的执行与普通函数一模一样,只要一行。
(2)更好的语义。async
表示函数里有异步操作,await
表示紧跟在后面的表达式需要等待结果。
(3)更广的适用性。async
函数的 await
命令后面,可以跟 Promise
对象和原始类型的值(数值、字符串和布尔值,但这时等同于同步操作)。
用法:
async
函数返回一个 Promise
对象,可以使用 then 方法添加回调函数。当函数执行的时候,一旦遇到 await
就会立即调用,等到触发的异步操作完成,再接着执行函数体内后面的语句。例如:
async function getStockPriceByName(name) { // 获取股票报价
var symbol = await getStockSymbol(name);
var stockPrice = await getStockPrice(symbol);
return stockPrice;
} // async关键字表明该函数内部有异步操作
getStockPriceByName('goog').then(function (result){
console.log(result);
}); // 调用该函数时会立即返回一个Promise对象
指定50毫秒后输出“hello world”
function timeout(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
async function asyncPrint(value, ms) {
await timeout(ms);
console.log(value)
}
asyncPrint('hello world', 50);
注意点
(1) await 命令后面的 Promise 对象,运行结果可能是 rejected,所以最好把 await 命令放在 try…catch 代码块中。
await
最后返回的结果是 resolve
,无法处理 reject
情况。
如果await关键字后面的Promise对象中没有执行resolve方法,就会导致Promise一直处在pending状态,无法执行then方法,因此await后面的代码不会执行。
错误处理:
function somethingThatReturnsAPromise() {
return new Promise.reject(1)
}
// 错误处理
// 写法一
async function myFunction() {
try {
await somethingThatReturnsAPromise();
} catch (err) {
console.log(err);
}
}
// 写法二
async function myFunction() {
await somethingThatReturnsAPromise().catch(function (err){
console.log(err);
});
}
// 写法三
function errPromise() {
return new Promise((resolve, reject) => {
reject(1)
}).catch(err => {
console.log('promise err: ', err)
})
}
async function myFunction() {
const res = await errPromise()
console.log('await result', res) // promise err: 1, await result: undefined
}
易错情况:
// 无法处理 reject 情况
async function test() {
await new Promise.reject(false)
console.log('执行结束') // 报错,无法执行到console
}
test()
// 改正
async function test() {
try {
await Promise.reject(false)
} catch(err) {
console.log('执行结束: ', err)
}
}
test() // 执行结束: false
(2) await
命令只能用在 async
函数之中,如果用在普通函数,就会报错。
但是,await
可以用于等待一个 async
函数的返回值,await
不仅等 promise
对象,也可以等任意表达式的结果。
async function dbFuc(db) {
let docs = [{}, {}, {}];
// 报错
docs.forEach(function (doc) { // 在function前加async关键字也有问题,db.post(doc)是并法的,而不是继发执行,应当采用for循环,而不是forEach
await db.post(doc);
});
}
// await 返回普通函数
function foo() {
return false
}
async function c() {
const res = await foo()
console.log('res: ', res)
}
c() // res: false
// await 返回普通包装函数
function a() {
return b()
}
function b() {
return new Promise((resolve, reject) => {
resolve(1) // 注意 当前结果只处理了 resolve 情况
})
}
async function c() {
const res = await a()
console.log('res: ', res)
}
c()
// 改进
function a() {
return b().catch(err => {
console.log('出错: ', err)
})
}
function b() {
return new Promise((resolve, reject) => {
resolve(1) // 如果是 reject(1) =》 输出结果是 出错: 1 res: undefined
})
}
async function c() {
const res = await a()
console.log('res: ', res)
}
c() // resolve 输出结果 res: 1
(3) 如果 async
函数返回非 Promise
对象,那么返回值会被封装成 Promise
对象。
// async 包装普通函数为 promise
async function bar() {
return 'test'
}
bar().then(res => {
console.log('res: ', res)
}) // res: test
(4) 使用promise.all
方法解决多个请求并发执行
在定义 promise 时函数就开始执行
// 写法一
async function dbFuc(db) {
let docs = [{}, {}, {}];
let promises = docs.map((doc) => db.post(doc));
let results = await Promise.all(promises);
console.log(results);
}
// 写法二
async function dbFuc(db) {
let docs = [{}, {}, {}];
let promises = docs.map((doc) => db.post(doc));
let results = [];
for (let promise of promises) {
results.push(await promise);
}
console.log(results);
}
使用Generators + Promise 实现 Await
将 Generator
(看似同步的异步代码)与 Promise
(可靠性和可组合性)组合起来。
function run(gen) {
var args = [].slice.call( arguments, 1), it;
// 在当前的上下文环境中初始化generator
it = gen.apply( this, args );
// 为generator的完成返回一个promise
return Promise.resolve()
.then( function handleNext(value){
// 运行至下一个让出的值
var next = it.next( value );
return (function handleResult(next){
// generator已经完成运行了?
if (next.done) {
return next.value;
}
// 否则继续执行
else {
return Promise.resolve( next.value )
.then(
// 在成功的情况下继续异步循环,将解析的值送回generator
handleNext,
// 如果`value`是一个拒绝的promise,就将错误传播回generator自己的错误处理
function handleErr(err) {
return Promise.resolve(
it.throw( err )
)
.then( handleResult );
}
);
}
})(next);
}
);
}
// 使用
// 创建一个Generator
function *foo() {
// axios返回一个promise
var r1 = yield axios( "http://some.url.1" );
var r2 = yield axios( "http://some.url.2" );
var r3 = yield axios(
"http://some.url.3/?v=" + r1 + "," + r2
);
console.log( r3 );
}
// 使用刚才定义的`run(..)`工具
run( foo );
代码为《你不知道的JavaScript(中卷)》中的示例代码
参考链接
阮一峰·async 函数的含义和用法:http://www.ruanyifeng.com/blog/2015/05/async.html
理解 JavaScript 的 async/await:https://segmentfault.com/a/1190000007535316