ES6 异步编程解决方案 之 Async
一、async
函数的基本用法
-
async
函数返回一个 Promise 对象,可以使用then
、catch
方法 添加回调函数 -
async
函数执行时,一旦遇到await
就会先返回,等到异步操作完成,再接着执行函数体内后面的语句 【异步函数 同步执行】 -
async
函数有很多种形式:
// 函数声明
async function foo() {}
// 函数表达式
const foo = async function () {};
// 箭头函数
const foo = async () => {};
// Class 的方法
class Storage {
constructor() {
this.cachePromise = caches.open('avatars');
}
async getAvatar(name) {
const cache = await this.cachePromise;
return cache.match(`/avatars/${name}.jpg`);
}
}
// 对象的方法
let obj = { async foo() {} };
obj.foo().then(...)
- 示例: 指定多少毫秒后输出一个值
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);
// 上面代码指定 50 毫秒以后,输出 hello world
二、async
语法
1. async
函数
-
async
函数的返回值 是 Promise 对象 (可调用then
、catch
方法) -
async
函数内部抛出错误,会导致返回的 Promise 对象变为reject
状态 -
async
函数的回调方法then
、catch
的参数-
async
函数内部return
关键字返回的值,会 作为then
、catch
方法的参数 -
async
函数的返回的 Promise 对象 状态为resolve
时,函数内部 若没有返回值,then
、catch
方法的参数 为undefined
;调用then
方法 -
async
函数的返回的 Promise 对象 状态为reject
时,函数内部 若没有返回值,then
、catch
方法的参数 为 错误对象;调用catch
方法
-
async function f() {
return await Promise.resolve(123);
}
f().then(v => console.log(v)); // 123
async function f() {
await Promise.resolve(123);
}
f().then(v => console.log(v)); // undefined
async function f() {
throw new Error('出错了');
}
f().then(
v => console.log(v),
e => console.log(e)
) // Error: 出错了
2. await
关键字
-
await
关键字 只能在async
函数中使用:await
后面 如果跟异步函数,程序执行时会等await
后面的 异步函数执行完之后,再执行 后面的代码 【异步函数 同步执行】 -
正常情况下,
await
命令后面是一个 Promise 对象;如果不是,会转成 Promise 对象,并立即resolve
// 只要有一个 `await` 后面的函数执行错误,程序就会停止执行代码
async function f() {
await Promise.reject('出错了');
await Promise.resolve('hello world'); // 不会执行
}
f().then(
(result) => {
console.log(result);
},
(e) => {
console.log(e);
}
); // 出错了
三、async
应用
1. 一个 async
函数中,有多个 await
关键字:使用 try..catch( )
避免程序终止
-
场景: 只要有一个
await
后面的函数执行错误,程序就会停止执行代码;async
函数返回的 Promise 对象的状态为reject
-
解决方案:
-
方案 1:使用
try...catch()
把有可能出错的 操作放到try
语句中 -
方案 2:
await
后面的 Promise 对象再跟一个catch
方法,处理前面可能出现的错误
-
// 解决方案 1:try...catch() 即使有一个异步操作失败,也不终止程序
async function f() {
try {
await Promise.reject('出错了');
} catch(e) {
console.log(e);
}
return await Promise.resolve('hello world');
}
f().then(v => console.log(v))
// hello world
// 解决方案 2:catch() 即使有一个异步操作失败,也不终止程序
async function f() {
await Promise.reject('出错了')
.catch(e => console.log(e));
return await Promise.resolve('hello world');
}
f().then(v => console.log(v))
// 出错了
// hello world
2. 一个 async
函数中,有多个 await
关键字:使 await
后面的异步操作 同时执行
// 写法一
let [foo, bar] = await Promise.all([getFoo(), getBar()]);
// 写法二
let fooPromise = getFoo();
let barPromise = getBar();
let foo = await fooPromise;
let bar = await barPromise;
3. 使用 try...catch()
结合 async
、await
实现 ajax 请求轮询
const NUM_RETRIES = 3;
async function ajaxRequest() {
for (let i = 0; i < NUM_RETRIES; ++i) {
try {
await $.get('http://google.com/this-throws-an-error');
break;
} catch(err) {
console.log(err);
}
}
}
ajaxRequest();
四、async for await...of
---> 异步遍历
- 遍历时,有一个异步请求报错,都会导致程序终止;所以,结合
try...catch()
解决此类问题
async function () {
try {
for await (const x of createRejectingIterable()) {
console.log(x);
}
}
catch (e) {
console.error(e);
}
}
五、async
与 其他异步解决方案的 比较
-
Promise: 比回调函数的写法大大改进,但是一眼看上去,代码完全都是 Promise 的 API(then、catch等等),操作本身的语义反而不容易看出来
-
Generator: 语义比 Promise 写法更清晰,需要有 任务运行器,自动执行 Generator 函数;但偏底层的写法
-
async: Generator 函数的语法糖,封装了 Generator 的底层机制 ,实现最简洁,最符合语义;