async函数
定义
是 Generator 函数的语法糖。
Generator 函数的引入为 JavaScript 中异步编程提供了一种更为直观和易于理解的方式
返回
async函数的返回值是Promise对象,
基本用法
使用 async
关键字声明的函数会自动返回一个 Promise 对象,无需手动创建。这使得异步函数更容易与其他异步操作协同工作。
async
函数返回一个 Promise 对象,可以使用then
方法添加回调函数。
当函数执行的时候,一旦遇到await就会先返回,等到异步操作完成,再接着执行函数体内后面的语句。
async function f() {
return 'hello world';
}
f().then(v => console.log(v))
// "hello world"
async函数内部return语句返回的值,会成为then方法回调函数的参数。
上面代码中,函数f
内部return
命令返回的值,会被then
方法回调函数接收到
async
函数内部抛出错误,会导致返回的 Promise 对象变为reject
状态。抛出的错误对象会被catch
方法回调函数接收到。
await命令
定义
正常情况下,await
命令后面是一个 Promise 对象,返回该对象的结果。如果不是 Promise 对象,就直接返回对应的值。
错误处理
如果await后面的异步操作出错,那么等同于async函数返回的Promise对象被reject。
防止出错的方法,也是将其放在try...catch
代码块之中
如果有多个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
命令后面的Promise
对象,运行结果可能是rejected
,所以最好把await
命令放在try...catch
代码块中。
第二点,多个await
命令后面的异步操作,如果不存在继发关系,最好让它们同时触发。
上面代码中,getFoo
和getBar
是两个独立的异步操作(即互不依赖),被写成继发关系。这样比较耗时,因为只有getFoo
完成以后,才会执行getBar
,完全可以让它们同时触发。
// 写法一
let [foo, bar] = await Promise.all([getFoo(), getBar()]);
// 写法二
let fooPromise = getFoo();
let barPromise = getBar();
let foo = await fooPromise;
let bar = await barPromise;
上面两种写法,getFoo
和getBar
都是同时触发,这样就会缩短程序的执行时间。
第三点,await
命令只能用在async
函数之中,如果用在普通函数,就会报错。
async 函数与 Promise的区别
- 返回值类型:
async
函数总是返回一个 Promise 对象。即使在函数内部使用return
返回非 Promise 类型的值,它也会被隐式地包装成一个解决的 Promise 对象。- 普通函数返回的是函数执行的结果,而
async
函数返回的是一个 Promise 对象。
- 语法简洁性:
async
函数的语法更为简洁,可以使用await
关键字来等待异步操作的完成,而不需要手动创建 Promise 链。Promise
需要通过.then()
或.catch()
来处理异步操作的结果,导致可能出现较深的嵌套(回调地狱)。
- 错误处理:
async
函数内部可以使用try...catch
来捕获和处理异步操作的错误,使得错误处理更加方便。- 在
Promise
中,错误处理通常通过.catch()
方法,或者在.then()
中的第二个参数进行。
- 使用场景:
async
函数适用于更直观地编写异步代码,特别是在处理多个异步操作时。Promise
通常用于处理单一的异步操作,或者在不使用async/await
语法的情况下进行异步编程。
await
的使用:await
只能在async
函数内部使用,用于等待一个 Promise 对象解决,并返回其解决值。- 在普通函数或全局作用域中,无法使用
await
关键字。
- 可读性:
- 使用
async/await
语法可以使异步代码更加直观和可读,避免了回调地狱,提高了代码的可维护性。 Promise
链的可读性较差,尤其是在处理多个异步操作时。
- 使用
综合来说,async/await
是一种更现代、更清晰的异步编程方式,而 Promise
仍然是底层的异步处理机制,可以在某些场景下继续发挥作用。通常,使用 async/await
更容易理解和维护异步代码。
理解
- 同步任务
操作在执行是会阻塞当前线程,只到该任务完成。
当遇到一个同步任务时,必须等待任务完毕,才继续往下执行
- 异步任务
不阻塞当前线程
常见的异步任务: 定时器(setTimeout/setInterbal)、网络请求、Promise、async/await
异步任务的结果通常通过回调函函数 async await等但会方式返回给主线程处理
// 同步任务
let result = longRunningSyncFunction();
console.log(result); // 这行代码会在longRunningSyncFunction执行完之后才执行
// 异步任务
longRunningAsyncFunction().then(result => {
console.log(result); // 这行代码会立即执行,然后在异步函数完成后得到结果
});
- async await
提供了直观和易于理解的方式
async函数返回的是Promise对象
防止出错一般包裹在try…catch中