这是我参与8月更文挑战的第4天,活动详情查看:8月更文挑战
话不多说,直接开干。。。
什么是 async 函数
- 很简单,像下面👇这样,普通函数前面加个
async
关键字,就是 async函数了。
async function fn() {
// 你的某些骚操作
await console.log('随便写什么东西')
}
怎么样,看到这是不是已经准备默默的开始鄙视😒了,就这。。。
看官别急,咱们继续。。。
async 函数就是 Generator 函数的语法糖。
- 同样的 async 也可以用来解决回调地狱的问题,而且它更像是同步的写法。
- 其具有以下的特点:
- 内置执行器(相对于Generator函数来说)
- 更好的语义
async
表示函数里有异步操作,await
表示紧跟在后面的表达式需要等待结果。
- 更广的适用性
async
函数的await
命令后面,可以是 Promise 对象和原始类型的值(数值、字符串和布尔值,但这时会自动转成立即 resolved 的 Promise 对象)。
- 返回值是Promise
async函数的返回值是Promise对象
。这也就意味着你可以用then方法指定接下来的操作。
用法
- async函数返回一个Promise对象,可以使用then方法添加回调函数。
- 当函数执行的时候,
一旦遇到 await 就会先返回
,等到异步操作完成,再接着执行函数体内后面的语句。
看到这,相信你已经知道了一些关于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 () => {};
返回promise对象
async
函数返回一个 Promise 对象。async
函数内部return
语句返回的值,会成为then
方法回调函数的参数。async
函数内部抛出错误,会导致返回的 Promise 对象变为reject
状态。抛出的错误对象会被catch
方法回调函数接收到。
Promise对象的状态变化
async
函数返回的 Promise 对象,必须等到内部所有await
命令后面的 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"
上述代码中:只有等 async函数getTitle体内的三个操作(抓取网页、取出文本、匹配标题)全部完成,才会执行then方法里边的console.log
await命令
-
正常情况下:
await
命令后面是一个 Promise 对象,返回该对象的结果。- 如果不是 Promise 对象,就直接返回对应的值。
-
另一种情况:
- await命令后面是一个 thenable对象(即定义了then方法的对象),那么await会将其等同于Promise对象
-
任何一个await语句后面的Promise对象变为reject状态,那么整个async函数都会中断执行。
async function f() {
await Promise.reject('出错了');
await Promise.resolve('hello world'); // 不会执行
}
此时,如果我们依然需用后续的await能够正常执行,就需要将第一个await放在try。。。catch结构中。
async function f() {
try {
await Promise.reject('出错了');
} catch(e) {
}
return await Promise.resolve('hello world');
}
f().then(val => console.log(val))
那么,还有别的处理方式吗?那必须有哇。 可以在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。
- 此时,使用try...catch结构包裹 await语句。
注意的点
- 如果多个await命令后面的异步操作,不存在依赖关系,最好让它们同时出发。如下:
// 写法一
let [foo, bar] = await Promise.all([getFoo(), getBar()]);
// 写法二
let fooPromise = getFoo();
let barPromise = getBar();
let foo = await fooPromise;
let bar = await barPromise;
-
await 只能用在 async函数中。
-
async 函数可以保留运行堆栈。
本文参考:es6入门
相关面试考题
async function async1(){
console.log('async1 start')
await async2()
console.log('async1 end')
}
async function async2(){
console.log('async2')
}
console.log('script start')
setTimeout(function(){
console.log('setTimeout')
},0)
async1();
new Promise(function(resolve){
console.log('promise1')
resolve();
}).then(function(){
console.log('promise2')
})
console.log('script end')
更全的可以移步这里:2021前端面试知识点总结之js篇
结束
别滑啦,到底啦,没啦。。。