Async函数
1.含义
ES2017引入Async函数,作为Generator函数的语法糖,使得异步操作更方便。
- Async函数的执行就像普通函数一样,调用即可执行。例:
asyncFunc(); - Async函数具有较好的语义,async表示函数内有异步操作,await表示其后的表达式需要等待结果。
- await关键字后可以是Promise对象,也可是原始类型的值(数值、字符串、布尔值,,但会转换成立即resolved的Promise对象。)
- async函数返回的是Promise对象,因此可以使用链式编程。 总结,async函数可以看成由多个异步操作包装成的一个Promise对象,await命令就是内部then命令的语法糖。
2.基本用法
async函数返回一个Promise对象,可以使用then方法添加回调函数。当async函数执行的时候,遇到await就先返回,等到其后的异步操作完成后,再继续执行函数体内后面的语句。
async函数有多种使用形式。
// 函数声明形式
async function foo(){}
// 函数表达式
let foo = async function (){}
// 作为对象的方法
let obj = {
async foo(){}
}
// Class 的方法
class Storage {
constructor() {
this.cachePromise = caches.open('avatars');
}
async getAvatar(name) {
const cache = await this.cachePromise;
return cache.match(`/avatars/${name}.jpg`);
}
}
// 箭头函数
let foo = async ()=> {}
3.语法
返回Promise对象。
- async函数返回一个Promise对象。
- async函数内部return的值,会成为then方法回调函数的参数。
async function foo() {
return 'Hello async!'
}
foo().then(res => {
console.log(res) // Hello async!
})
async函数内部抛出错误,会导致返回的Promise对象变为rejected状态。而抛出的错误对象会被catch方法的回调函数接收到。
async function foo() {
throw new Error('error')
}
// foo().then((res) => {
// console.log(res)
// },(err) => {
// console.log(err) // Error: error
// })
foo().then((result) => {
console.log(result)
}).catch((err) => {
console.log(err)
})
Promise对象的状态变化
async函数内部所有await命令后的Promise对象执行完后,async函数返回的Promise对象的状态才会发生改变,除非函数内部return或抛出错误。
即,async函数内部所有的异步操作执行完后才会执行then方法中的回调函数。
// 将就学习案例
async function getTitle(url) {
let response = await fetch(url);
let html = await response.text();
return html.match(/<title>([\s\S]+)<\/title>/i)[1];
}
getTitle('https://tc39.github.io/ecma262/').then(console.log)
// "ECMAScript 2017 Language Specification"
await命令
正常情况,await后面是Promise对象,返回该对象的结果。如果不是Promise对象,则直接返回对应的值。
async function foo(){
// return 123
return await 123
}
foo().then((params) => {
console.log(params) // 123
})
await命令后面是一个thenable对象(即定义了then方法的对象),那么await会将其等同于 Promise 对象。
class Sleep {
constructor(timeout) {
this.timeout = timeout;
}
then(resolve, reject) {
const startTime = Date.now();
setTimeout(
() => resolve(Date.now() - startTime),
this.timeout
);
}
}
(async () => {
const sleepTime = await new Sleep(1000);
console.log(sleepTime);
})();
// 1000
/*上面代码中,await命令后面是一个Sleep对象的实例。这个实例不是 Promise 对象,但是因为定义了then方法,await会将其视为Promise处理。
这个例子还演示了如何实现休眠效果。JavaScript 一直没有休眠的语法,但是借助await命令就可以让程序停顿指定的时间。下面给出了一个简化的sleep实现。*/
function sleep(interval) {
return new Promise(resolve => {
setTimeout(resolve, interval);
})
}
// 用法
async function one2FiveInAsync() {
for(let i = 1; i <= 5; i++) {
console.log(i);
await sleep(1000);
}
}
one2FiveInAsync();
await命令后的Promise对象若变为reject状态,则reject抛出的值会被catch方法的回调函数接收到。
async function foo() {
await Promise.reject('error')
}
foo().then((res) => {
}).catch((err) => {
console.log(err) // error
})
// await语句前面没有return。加上return语句效果也是一样的。
任何一个await语句后的Promise对象变为reject状态,都会中断async函数的执行。
async function foo() {
await Promise.reject('error')
await Promise.resolve('value') // 这句不会执行。
}
使用try...catch,即使前面的异步操作失败,也不会中断async函数的执行。或者在前面的异步操作后使用catch捕捉前面产生的错误。
async function foo() {
try {
await Promise.reject('Error')
} catch (error) {
}
return await Promise.resolve('Value')
}
foo().then(res => {
console.log(res) // Value
})
async function foo() {
await Promise.reject('Error').catch(err => {
console.log(err)
})
return await Promise.resolve('Value')
}
foo().then(res => {
console.log(res)
})
// Error
// Value
错误处理
await命令后的异步操作出错,即等同于async函数返回的Promise被reject。
async function foo() {
return new Promise((resolve,reject) => {
throw new Error('error')
})
}
foo().then((res) => {
}).catch((err) => {
console.log(err) // Error:error
})
防止出错的方法,将其放入try...catch语句之中。
async function foo() {
try {
await new Promise((resolve,reject) => {
throw new Error('error')
})
} catch (error) {
// dosomething
}
}
多个await命令,可以统一放入try...catch之中
// 原例子
async function main() {
try {
const val1 = await firstStep();
const val2 = await secondStep(val1);
const val3 = await thirdStep(val1, val2);
console.log('Final: ', val3);
}
catch (err) {
console.error(err);
}
}
使用注意点 (未完。。。)
- 最好将await命令放入try...catch之中。