async 函数
概念
是 Generator 函数(生成器函数)的语法糖。
特点
与 Generator 函数的改进体现在:
1,内置执行器
2,更好的语义
async 和 await 比起星号和 yield ,语义更清楚。async 表示函数有异步操作,await 表示紧跟在后面的表达式需要等待结果
3,更广的适用性
co 模块规定,yield 命令后面只能是 Thunk 函数或者 Promise 对象,而 async 函数的 await 命令后面可以是 Promise 对象,和原始类型值(数值,字符串和布尔值,但这时等于同步操作)。
4,返回的是 Promise 对象
比 Generator 函数的返回值 是 Iterator 对象更方便。可以用 then 方法指定下一步操作。
async 函数完全可以看做是由多个异步操作包装成一个 Promise 对象,而 await 命令就是 内部 then 命令的语法糖。
语法
async 函数的语法规则总体上来说比较简单,难点在于错误处理机制。
返回 Promise对象
async 函数返回一个 Promise 对象,内部的 return 语句返回的值,会成为 then 方法回调函数的参数。
async function fn() {
return 'hello world';
}
fn().then( v => {
console.log(v);
})
async 函数内部抛出错误会导致返回的 Promise 对象变成 reject 转态。抛出的错误对象会被 catch 方法回调函数接收。
async function fn() {
throw new Error('出错了!');
}
fn().then(
success => console.log(success),
error => console.log(error)
)
Promise 对象的状态变化
async 函数返回的 Promise 对象必须等到内部所有 await 命令后面的 Promise 对象执行完才会发生状态改变,除非遇到 return 语句或者抛出错误。也就是说,只有 async 函数内部的异步操作执行完成,或者发生错误,才会执行 then 方法指定的回调函数。
async function getTitle(url) {
let response = await fetch(url);
let html = awaiit response.text();
return html.match(/<title>([\s\S])</title>/)[1];
}
getTitle('www.baidu.com').then(v => console.log(v));
上述代码,函数 getTitle() 内部有三个 操作:抓取网页,取出文本,匹配页面标题。只有这 3个 操作全部完成,才会执行 then 方法里面的 console.log()
await 命令
正常情况下,await 命令后面是一个 Promise 对象,如果不是,会被转成一个立即 resolve 的 Promise 对象。
async function fn() {
return await 123;
}
fn().then(v => console.log(v)); //123
await 命令后面的 Promise 对象如果变成 reject 状态,则 reject 的参数会被 catch 方法的回调函数接收到。
async function fn() {
return await Promise.reject('出错了!');
}
fn().then(v => console.log(v))
.catch(err => console.log(err))
只要有一个 await 语句后面的 Promise 变成 reject,那么整个 async 函数都会中断执行。
async function fn() {
return await Promise.reject('出错了!');
await Promise.resolve('hello world'); // 不会执行
}
fn().then(v => console.log(v))
.catch(err => console.log("err =>", err))
错误处理
如果 await 命令后面的异步操作出错,那么等同于 async 函数返回的 Promise 对象被 reject 。
async function fn() {
return await new Promise((resolve, reject) => {
throw new Error('异步操作出错了');
})
}
fn().then(v => console.log(v))
.catch(err => console.log("err =>", err))
防止出错的方法也是将其放到 try ... catch 代码中。
async function fn() {
try{
await new Promise((resolve, reject) => {
throw new Error('异步操作出错了');
})
}catch(e){
//TODO handle the exception
}
return await('hello world');
}
fn().then(v => console.log(v))
.catch(err => console.log("err =>", err))
如果有多个 await 命令,则可以统一放在 try ... catch 结构中。
用法
当函数执行的时候,一旦遇到 await 就会先返回,等到异步操作完成,再接着执行函数体后面的语句。async 函数返回一个 Promise 对象,可以使用 then 方法添加回调函数。
例子:
async function getStockPriceByName(name) {
let symbol = await getStockSymbol(name);
let price = await getStockPrice(symbol);
return price;
}
getStockByName('good').then(function(result) {
console.log(result);
})
使用注意点
1,await 命令后面的 Promise 对象的运行结果可能是 rejected,所以最好把 await 命令放到 try ...catch 代码块中。
2,多个 await 命令后面的异步操作如果不存在继发关系,最好让它们同时触发。
// 写法一
let fooPromise = getFoo();
let barPromise = getBar();
let foo = await fooPromise;
let bar = await barPromise;
// 写法二
let [foo, bar] = await Promise.all([getFoo(), getBar()])
3,await 命令只能用在 async 函数中,如果用在普通函数中就会报错;forEach() 方法中 使用 async 函数 和 await 命令也会报错,
需要改成 for 循环。