博主简介:知名前端工程师!
出没地点:重庆-沙坪坝
2022年目标:成为一个大佬
✒️ 最近在做的事情:学习「web前端开发」!———————————————————————————————————————————
版权声明:本文为 CSDN 博主「Lady Marry
」的原创文章,转载请附上原文出处链接及本声明。
ES2017 标准引入了 async 函数,使得异步操作变得更加方便。async 和 await 是用来处理异步操作的,把异步变为同步的一种方法。async/await 的目的为了简化使用基于 promise 的 API 时所需的语法。async/await 的行为就好像搭配使用了生成器和 promise。
如果这些文字理论看不出来 async/await 有啥用,可以直接看最后的综合案例就懂了
async
函数返回一个 Promise 对象。async
函数内部return
语句返回的值,会成为then
方法回调函数的参数。
async function f() {
return 'hello world';
}
f().then(v => console.log(v))
// "hello world"
async
函数内部抛出错误,会导致返回的 Promise 对象变为reject
状态。抛出的错误对象会被catch
方法回调函数接收到。
async function f() {
throw new Error('出错了');
}
f().then(
v => console.log('resolve', v),
e => console.log('reject', e)
)
//reject Error: 出错了
async 函数有多种使用形式。
// 函数声明
async function foo() {}
// 函数表达式
const foo = async function () {};
// 对象的方法
let obj = { async foo() {} };
obj.foo().then(...)
// Class 的方法
class Storage {
constructor() {
this.cachePromise = caches.open('avatars');
}
async getAvatar(name) {
const cache = await this.cachePromise;
return cache.match(`/avatars/${name}.jpg`);
}
}
const storage = new Storage();
storage.getAvatar('jake').then(…);
// 箭头函数
const foo = async () => {};
正常情况下,await
命令后面是一个 Promise 对象,返回该对象的结果。如果不是 Promise 对象,就直接返回对应的值。
async function f() {
// 等同于
// return 123;
return await 123;
}
f().then(v => console.log(v))
// 123
await
命令后面的 Promise 对象如果变为reject
状态,则reject
的参数会被catch
方法的回调函数接收到。
async function f() {
return await Promise.reject('出错了');
}
f()
.then(v => console.log(v))
.catch(e => console.log(e))
// 出错了
任何一个await
语句后面的 Promise 对象变为reject
状态,那么整个async
函数都会中断执行。
async function f() {
await Promise.reject('出错了');
await Promise.resolve('hello world'); // 不会执行
}
有时,我们希望即使前一个异步操作失败,也不要中断后面的异步操作。这时可以将第一个await
放在try...catch
结构里面,这样不管这个异步操作是否成功,第二个await
都会执行。
async function f() {
try {
await Promise.reject('出错了');
} catch(e) {
}
return await Promise.resolve('hello world');
}
f()
.then(v => console.log(v))
// hello world
另一种方法是await
后面的 Promise 对象再跟一个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
如果await
后面的异步操作出错,那么等同于async
函数返回的 Promise 对象被reject
。
async function f() {
await new Promise(function (resolve, reject) {
throw new Error('出错了');
});
}
f()
.then(v => console.log(v))
.catch(e => console.log(e))
// Error:出错了
防止出错的方法,也是将其放在try...catch
代码块之中。
async function f() {
try {
await new Promise(function (resolve, reject) {
throw new Error('出错了');
});
} catch(e) {
}
return await('hello world');
}
如果有多个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);
}
}
如果确实希望多个请求并发执行,可以使用Promise.all
方法
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);
}
早期的语法规定是,await
命令只能出现在 async 函数内部,否则都会报错。从 ES2022 开始,允许在模块的顶层独立使用await命令,它的主要目的是使用await解决模块异步加载的问题。
// import() 方法加载
const strings = await import(`/i18n/${navigator.language}`);
// 数据库操作
const connection = await dbConnector();
let jQuery;
try {
jQuery = await import('https://cdn-a.com/jQuery');
} catch {
jQuery = await import('https://cdn-b.com/jQuery');
}
注意,顶层
await
只能用在 ES6 模块
比如我们需要按顺序获取:产品数据=>用户数据=>评论数据,该怎么写呢?
萌新
// 获取产品数据
ajax('products.json', (products) => {
console.log('products >>>', JSON.parse(products));
// 获取用户数据
ajax('users.json', (users) => {
console.log('users >>>', JSON.parse(users));
// 获取评论数据
ajax('comments.json', (comments) => {
console.log('comments >>>', JSON.parse(comments));
});
});
});
白银
// 封装,返回一个 Promise
function requestP(url) {
return new Promise(function(resolve, reject) {
$.getJSON(url, (data) => {
iterator.next(data);
});
});
}
// 获取产品数据
requestP('products.json').then(function(products){
console.log('products >>>', products);
});
// 获取用户数据
requestP('users.json').then(function(users){
console.log('users >>>', users);
});
// 获取评论数据
requestP('comments.json').then(function(comments){
console.log('comments >>>', comments);
});
白银一
// Generators
function request(url) {
$.getJSON(url, (data) => {
iterator.next(data);
});
}
function *main() {
// 获取产品数据
let products = yield request('products.json');
// 获取用户数据
let users = yield request('users.json');
// 获取评论数据
let comments = yield request('comments.json');
console.log('products >>>', products);
console.log('users >>>', users);
console.log('comments >>>', comments);
}
var iterator = main();
iterator.next();
黄金
// 封装 Ajax,返回一个 Promise
function requestP(url) {
return new Promise(function(resolve, reject) {
ajax(url, (response) => {
resolve(JSON.parse(response));
});
});
}
(async () => {
// 获取产品数据
let products = await requestP('products.json');
// 获取用户数据
let users = await requestP('users.json');
// 获取评论数据
let comments = await requestP('comments.json');
console.log('ES7 Async/products >>>', products);
console.log('ES7 Async/users >>>', users);
console.log('ES7 Async/comments >>>', comments);
}());