Skip to content
字数
773 字
阅读时间
4 分钟

await 命令后面的 Promise 对象,运行结果可能是 rejected,如果 await 后面还有语句也不会再执行

所以最好把 await 命令放在 try...catch 代码块中

错误抛出 ​

async 函数内部抛出错误,会导致返回的 Promise 对象变为 reject 状态
抛出的错误对象会被 catch 方法回调函数接收到

Promise 对象的状态变化 ​

async 函数返回的 Promise 对象,必须等到内部所有 await 命令后面的 Promise 对象执行完,才会发生状态改变,除非遇到 return 语句或者 抛出错误

也就是说,只有 async 函数内部的异步操作执行完,才会执行 then 方法指定的回调函数

使用注意点 ​

多个 await ​

多个 await 命令后面的异步操作,如果不存在继发关系,最好让它们同时触发

js
let foo = await getFoo();
let bar = await getBar();

getFoo 和 getBar 是两个独立的异步操作(即互不依赖),被写成继发关系。这样比较耗时,因为只有 getFoo 完成以后,才会执行 getBar,完全可以让它们同时触发。

javascript
// 写法一
let [foo, bar] = await Promise.all([getFoo(), getBar()]);

// 写法二
let fooPromise = getFoo();
let barPromise = getBar();
let foo = await fooPromise;
let bar = await barPromise;

async 函数可以保留运行堆栈 ​

javascript
const a = () => {
  b().then(() => c());
};

上面代码中,函数 a 内部运行了一个异步任务 b()。当 b() 运行的时候,函数 a() 不会中断,而是继续执行。等到 b() 运行结束,可能 a() 早就运行结束了,b() 所在的上下文环境已经消失了。如果 b() 或 c() 报错,错误堆栈将不包括 a()。

现在将这个例子改成 async 函数。

javascript
const a = async () => {
  await b();
  c();
};

上面代码中,b() 运行的时候,a() 是暂停执行,上下文环境都保存着。一旦 b() 或 c() 报错,错误堆栈将包括 a()。

顶层使用 await ​

主要目的是使用 await 解决模块异步加载的问题

下面是顶层 await 的一些使用场景。

javascript
// 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 命令的模块,加载命令是同步执行的。

javascript
// x.js
console.log("X1");
await new Promise(r => setTimeout(r, 1000));
console.log("X2");

// y.js
console.log("Y");

// z.js
import "./x.js";
import "./y.js";
console.log("Z");

上面代码有三个模块,最后的 z.js 加载 x.js 和 y.js,打印结果是 X1、Y、X2、Z。这说明,z.js 并没有等待 x.js 加载完成,再去加载 y.js。

顶层的 await 命令有点像,交出代码的执行权给其他的模块加载,等异步操作完成后,再拿回执行权,继续向下执行。

参考 ​

async 函数

贡献者

The avatar of contributor named as jiechen jiechen

页面历史

用 ♥ 撰写