【问题标题】:Use a top-level await, if supported by the current runtime如果当前运行时支持,请使用顶级等待
【发布时间】:2021-08-27 13:46:37
【问题描述】:

在 14.3.0 中通过--experimental-top-level-await 和后来的--harmony-top-level-await 向 Node.js 添加了顶级等待支持。

问题

如果当前 Node.js 运行时支持,我需要在我的 ESM 脚本文件中使用顶级 await。此外,我需要设置一个布尔标志来指示承诺已在顶层成功等待。

我的意思的一个例子:

let topLevelAwaitEnabled;
try {
    await Promise.resolve(); // replaced with an actual promise
    topLevelAwaitEnabled = true;
} catch (ignored) {
    topLevelAwaitEnabled = false;
}
console.log(topLevelAwaitEnabled);

// carry on with the rest of the application, regardless of success or failure
// at some point, topLevelAwaitEnabled is checked to conditionally execute some code

如果启用顶级等待支持,则成功。 然而,如果不支持,这将在解析期间导致以下错误,并且无法在运行时try/捕获catch:

$ node test.js...\test.js:3
    await Promise.resolve(); // replaced with an actual promise
    ^^^^^

SyntaxError: await is only valid in async function

所以问题是:我如何使用顶级等待如果支持它,而不会与不支持顶级等待的 Node.js 运行时产生不兼容问题(要么没有 CLI 标志指定或根本没有运行时支持)?

如果答案是“不可能”,我想解释一下为什么这是不可能的。


在我实际提交XY problem 的情况下,根本问题是我需要顶级动态导入。

注意:我很清楚出于各种原因不建议使用顶级 await,但它对于我的应用程序的特定功能至关重要,并且不会对我的用例造成任何问题.替代品可能不够用。

尝试

我尝试了以下方法,均无效:

  • eval:我尝试将 await 行替换为 eval("await Promise.resolve()"),希望在当前上下文中对代码进行评估。不幸的是,即使支持顶级 await,这也会导致相同的错误,因为它似乎没有继承当前上下文。
  • vm.compileFunction:同样的问题是eval(),不支持顶级等待。
  • vm.SourceTextModule:评估是异步的,需要在顶层等待以检查它是否受支持......这是一个问题 22。
  • 基于process.versionprocess.execArgvawait 条件执行:解析过程中的错误 - 它从未真正执行代码,因此排除了条件执行。

【问题讨论】:

  • 这里有你不使用 .then 的原因吗?如果您说问题是因为您需要顶级动态导入,那么似乎 then 而不是 await 会起作用,不是吗?
  • @ChaimFriedman 我正在实现一个 ESM loader polyfill,它可以动态导入其他 ESM 加载器以模拟 ESM 加载器链接 (not currently supported)。不幸的是,getGlobalPreloadCode() 钩子不是异步的,它会在我的脚本加载后立即调用。由于动态导入是异步的,我无法委托 getGlobalPreloadCode() 调用其他 ESM 加载器,因为它们尚未加载。剩下的钩子是异步的,我可以在钩子里面等待。

标签: node.js async-await top-level-await


【解决方案1】:

据我所知,这是不可能的,因为解析器只会出错。编译器将无法理解await 指令并且不会完成其循环。这可能类似于使用一个根本无法识别的关键字。

你能得到的最接近的是使用匿名函数。

【讨论】:

  • 那是相当不幸的。如果在合理的时间范围内没有答案能够证明其他情况,我会将其标记为已接受的答案。在其他关键字的情况下,eval() 将能够在运行时正确验证关键字是否受支持,因为不关心异步行为。
  • 我能想到的唯一其他选择是使用某种预处理器来确定 nodejs 的版本,并使用顶级等待或不使用备份覆盖您的文件以及分配变量。
【解决方案2】:

似乎您可以像process.version 那样检查运行时使用的节点版本,然后您还可以使用process.argv 检查启动进程时传入的任何标志。

使用上述方法,你可以先检查正在使用的节点的版本,然后如果相关,你可以检查需要的标志。

节点 docs 正在检查 CLI 参数。

【讨论】:

  • 感谢您的快速回复。不幸的是,这似乎不起作用,因为无论是否使用if 有条件地执行,await 都会出错。我对此进行了调查作为替代方案,但最终它似乎导致了相同的潜在问题 - 解析器不欣赏await,无论它位于何处。它永远无法到达代码块或执行代码。如果是这样,这将是一个完全可行的解决方案。
  • 是的,只是在回答后测试了我自己的理论,正如你所说,解析器不允许顶级等待出现在错误的版本中。这确实是有道理的,因为节点不会 1 传递您的代码并在遇到您的代码时执行。在实际运行代码之前,它将至少执行一次“编译”传递。如果在此过程中遇到无法编译的东西,它会爆炸
【解决方案3】:
(async()=>{await promise})()

【讨论】:

  • 这不是top level await,很遗憾不能满足我的用例。
  • 嗯..我明白了,所以...等待在正常的异步选项卡中不起作用??有趣的gggg
猜你喜欢
  • 2021-09-27
  • 1970-01-01
  • 2022-09-28
  • 1970-01-01
  • 1970-01-01
  • 2021-09-21
  • 2023-03-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多