top-level await

3,623 阅读3分钟

我们都知道在异步编程中, await 只能在 aysnc function 里进行使用。await 提升了异步编程的体验,使我们能像同步函数那样处理异步函数。同时我们也会好奇 await 只能在 aysnc 中使用

ECMAScript 提案 Top-level await 允许开发者在 async 函数外使用 await 字段,目前已进入 tc39 Stage 3。

Top-level await 允许你将整个 JS 模块视为一个巨大的 async 函数,这样就可以直接在顶层使用 await,而不必用 async 函数包一层。

早在2020-08-11 node 发布 14.8.0 版本开始支持top-level await

1. 在引入top-level await之前

// ------ method.js
export function double(num) {
  return num * 2;
}

export function square(num) {
  return num * num;
}

// ------ middleware.js
import { double, square } from "./method.js";

let doubleOutput;
let squareOutput;

// IIFE
(async () => {
  await requestData();
  doubleOutput = double(10);
  squareOutput = square(10);
})();

// 模拟接口请求
function requestData(delays = 1000) {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(console.log("❤️"));
    }, delays);
  });
}

export { doubleOutput, squareOutput };

// ------ index.js
import { doubleOutput, squareOutput } from "./middleware.js";

console.log("doubleOutput-init", doubleOutput); // undefined
console.log("squareOutput-init", squareOutput); // undefined

setTimeout(() => console.log("doubleOutput-delay", doubleOutput), 2000); // 20
setTimeout(() => console.log("squareOutput-delay", squareOutput), 2000); // 100

### 2.使用 Export Promise 来代替初始化
// ------ method.js
export function double(num) {
  return num * 2;
}

export function square(num) {
  return num * num;
}

// ------ middleware.js
import { double, square } from "./method.js";

let doubleOutput;
let squareOutput;

// IIFE
export default (async () => {
  await requestData();
  doubleOutput = double(10);
  squareOutput = square(10);
  return { doubleOutput, squareOutput };
})();

// 模拟接口请求
function requestData(delays = 1000) {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(console.log("❤️"));
    }, delays);
  });
}

export { doubleOutput, squareOutput };

// ------ index.js
import promise from "./middleware.js";

promise.then(({ doubleOutput, squareOutput }) => {
  console.log("doubleOutput-delay", doubleOutput); // 20
  console.log("squareOutput-delay", squareOutput); // 100
});

所有引用都要写成 promise 结构,十分不方便

3. 使用top-level await

// ------ method.js
export function double(num) {
  return num * 2;
}

export function square(num) {
  return num * num;
}

// ------ middleware.js
import { double, square } from "./method.js";

let doubleOutput;
let squareOutput;

// "plugins": ["@babel/plugin-syntax-top-level-await"]
await requestData();

doubleOutput = double(10);
squareOutput = square(10);

// 模拟接口请求
function requestData(delays = 1000) {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(console.log("❤️"));
    }, delays);
  });
}

export { doubleOutput, squareOutput };

// ------ index.js
import { doubleOutput, squareOutput } from "./middleware.js";

console.log("doubleOutput-init", doubleOutput); // 20
console.log("squareOutput-init", squareOutput); // 100

4. 过去

async/await 首次引用时,尝试在 async 函数外部使用 await 的结果是产生 SyntaxError。大多数开发者使用立即执行异步函数表达式的方式来使用该功能。

await Promise.resolve(console.log('🎉'));
// → SyntaxError: await is only valid in async function
(async function () {
  await Promise.resolve(console.log('🎉'));
  // → 🎉
})();

5. 现在

在 top-level await 的支持下,下面代码可以替换模块中常见代码

await Promise.resolve(console.log('🎉'));
// → 🎉

注意: top-level await 仅能工作在模块的顶层。在 class 代码块或非 async 函数不支持。

6. 何时使用

参考 spec proposal repository

6.1 动态依赖导入

const strings = await import(`/i18n/${navigator.language}`);

这允许在模块的运行时环境中确认依赖项。

6.2 资源初始化

const connection = await dbConnector();

允许模块申请资源,同时也可以在模块不能使用时抛出错误。

6.3 依赖回退

let jQuery;
try {
  jQuery = await import('https://cdn-a.example.com/jQuery');
} catch {
  jQuery = await import('https://cdn-b.example.com/jQuery');
}

希望从 CDN A 加载 JavaScript 库,如果它加载失败,将加载 CDN B

结尾

top-level await 在某些特定场景有很方便的作用,但是目前这个一特性还没有很好的运用到生产代码中

本文整理自作者 Myles Borins 「Top-level await」,转载请注明来源链接

v8.dev/features/to…

相关资源: