概要:在本教程中,您将了解 JavaScript 顶层 await 及其用例。
JavaScript 顶层 await 简介
ES2020 引入了顶层 await 功能,允许模块的行为类似于 async
函数。导入顶层 await 模块的模块将等待它加载,然后再评估其主体。
为了更好地理解顶层 await 功能,我们将举一个例子
在这个例子中,我们将有三个文件:index.html
、app.mjs
和 user.mjs
index.html
使用app.mjs
文件。app.mjs
导入user.mjs
文件。user.mjs
从具有 URL 终结点 https://jsonplaceholder.typicode.com/users 的 API 中获取 JSON 格式的用户数据
以下是使用 app.mjs 模块的索引文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JavaScript Top-Level Await Demo</title>
</head>
<body>
<div class="container"></div>
<script type="module" src="app.mjs"></script>
</body>
</html>
Code language: HTML, XML (xml)
以下显示了 user.mjs
文件
let users;
(async () => {
const url = 'https://jsonplaceholder.typicode.com/users';
const response = await fetch(url);
users = await response.json();
})();
export { users };
Code language: JavaScript (javascript)
user.mjs
模块使用 fetch API 从 API 中获取 JSON 格式的用户并导出它。
因为我们只能在 async
函数(ES2020 之前)中使用 await
关键字,所以我们需要将 API 调用包装在一个立即调用的异步函数表达式 (IIAFE) 中。
以下是 app.mjs
模块
import { users } from './user.mjs';
function render(users) {
if (!users) {
throw 'The user list is not available';
}
const list = users
.map((user) => {
return `<li> ${user.name}(<a href="email:${user.email}">${user.email}</a>)</li>`;
})
.join('');
return `<ol>${list}</ol>`;
}
const container = document.querySelector('.container');
try {
container.innerHTML = render(users);
} catch (e) {
container.innerHTML = e;
}
Code language: JavaScript (javascript)
它是如何工作的。
首先,从 user.mjs
模块导入 users
import { users } from './user.mjs';
Code language: JavaScript (javascript)
其次,创建一个 render()
函数,将用户列表渲染到 HTML 格式的有序列表中
function render(users) {
if (!users) {
throw 'The user list is not available.';
}
const list = users
.map((user) => {
return `<li> ${user.name}(<a href="email:${user.email}">${user.email}</a>)</li>`;
})
.join('');
return `<ol>${list}</ol>`;
}
Code language: JavaScript (javascript)
第三,将用户列表添加到具有类 .container
的 HTML 元素中
const container = document.querySelector('.container');
try {
container.innerHTML = render(users);
} catch (e) {
container.innerHTML = e;
}
Code language: JavaScript (javascript)
如果你打开 index.html
,你会看到以下消息
The user list is not available.
Code language: PHP (php)
以下显示了主要流程
在这个流程中
- 首先,
app.mjs
导入user.mjs
模块。 - 其次,
user.mjs
模块执行并进行 API 调用。 - 第三,当第二步仍在进行时,
app.mjs
开始使用从user.mjs
模块导入的users
数据。
由于步骤 2 尚未完成,因此 users
变量为 undefined
。因此,你在页面上看到了错误消息。
解决方法
要解决此问题,您可以从 user.mjs
模块导出一个 Promise
,并在使用其结果之前等待 API 调用完成。
以下显示了 user.mjs
模块的新版本
let users;
export default (async () => {
const url = 'https://jsonplaceholder.typicode.com/users';
const response = await fetch(url);
users = await response.json();
})();
export { users };
Code language: JavaScript (javascript)
在这个新版本中,user.mjs 模型导出 users
和一个 Promise
作为默认导出。
在 app.mjs
中,从 user.mjs
文件导入 promise
和 users
,并按如下方式调用 then()
方法:promise
import promise, { users } from './user.mjs';
function render(users) {
if (!users) {
throw 'The user list is not available.';
}
let list = users
.map((user) => {
return `<li> ${user.name}(<a href="email:${user.email}">${user.email}</a>)</li>`;
})
.join(' ');
return `<ol>${list}</ol>`;
}
promise.then(() => {
let container = document.querySelector('.container');
try {
container.innerHTML = render(users);
} catch (error) {
container.innerHTML = error;
}
});
Code language: JavaScript (javascript)
它是如何工作的。
首先,从 user.mjs
模块导入 promise
和 users
import promise, { users } from './user.mjs';
Code language: JavaScript (javascript)
其次,调用 promise 的 then()
方法,等待 API 调用完成以使用其结果
promise.then(() => {
let container = document.querySelector('.container');
try {
container.innerHTML = render(users);
} catch (error) {
container.innerHTML = error;
}
});
Code language: JavaScript (javascript)
现在,如果你打开 index.html
,你会看到一个用户列表。但是,你需要知道要等待的正确 promise,才能使用它。
ES2022 在此解决方法中引入了顶层 await 模块来解决此问题。
使用顶层 await
首先,将 user.mjs
更改为以下内容
const url = 'https://jsonplaceholder.typicode.com/users';
const response = await fetch(url);
let users = await response.json();
export { users };
Code language: JavaScript (javascript)
在这个模块中,你可以使用 await
关键字,而无需将语句放在 async
函数中。
其次,从 user.mjs
模块导入 users 并使用它
import { users } from './user.mjs';
function render(users) {
if (!users) {
throw 'The user list is not available.';
}
let list = users
.map((user) => {
return `<li> ${user.name}(<a href="email:${user.email}">${user.email}</a>)</li>`;
})
.join(' ');
return `<ol>${list}</ol>`;
}
let container = document.querySelector('.container');
try {
container.innerHTML = render(users);
} catch (error) {
container.innerHTML = error;
}
Code language: JavaScript (javascript)
在这种情况下,app.mjs
模块将在执行其主体之前等待 user.mjs
模块完成。
JavaScript 顶层 await 用例
什么时候使用顶层 await?以下是一些用例。
动态依赖路径
const words = await import(`/i18n/${navigator.language}`);
Code language: JavaScript (javascript)
在这个例子中,顶层 await 允许模块使用运行时值来决定依赖项,这对于以下场景很有用
- 国际化 (i18n)
- 开发/生产环境拆分。
依赖项回退
在这种情况下,您可以使用顶层 await 从服务器(cdn1)加载模块。如果失败,您可以从备份服务器(cdn2)加载它
let module;
try {
module = await import('https://cdn1.com/module');
} catch {
module = await import('https://cdn2.com/module');
}
Code language: JavaScript (javascript)
概要
- 顶层 await 模块的行为类似于
async
函数。 - 当模块导入顶层 await 模块时,它会等待顶层 await 模块完成,然后再评估其主体。