在异步函数之外使用 await

Posted

技术标签:

【中文标题】在异步函数之外使用 await【英文标题】:Using await outside of an async function 【发布时间】:2017-02-02 10:04:43 【问题描述】:

我试图将两个异步函数链接在一起,因为第一个函数有一个条件返回参数,导致第二个函数运行或退出模块。但是,我发现了在规范中找不到的奇怪行为。

async function isInLobby() 
    //promise.all([chained methods here])
    let exit = false;
    if (someCondition) exit = true;

这是我的代码的混蛋 sn-p(您可以查看完整范围 here),它只是检查玩家是否已经在大厅中,但这无关紧要。

接下来我们有这个异步函数。

async function countPlayer() 
    const keyLength = await scardAsync(game);
    return keyLength;

如果exit === true,则不需要运行此函数。

我试过了

const inLobby = await isInLobby();

我希望这会等待结果,所以我可以使用inLobby 有条件地运行countPlayer,但是我收到了一个没有具体细节的类型错误。

为什么你不能await一个async函数范围之外的函数?我知道这是一个糖承诺,所以它必须链接到then,但是为什么在countPlayer我可以等待另一个承诺,但在外面,我不能awaitisInLobby

【问题讨论】:

您能告诉我们您在哪里您在await isInLobby() 上做的,以及inLobby 是如何使用的吗?另外,countPlayer 在哪里/如何调用? @Bergi 我将我的存储库链接到实际上下文。太多代码无法放入问题中 我看不出问题出在哪里(也许你已经更新了 repo)?如果您参考isInLobby().then( … countPlayer().then … 部分,则解决方案很简单:只需制作包含这些调用的函数((req, res) => 之一)async @Bergi 问题不在于它被破坏了,它按原样工作。我只是不明白为什么***等待不是一件事。事实证明,如果不将整个模块定义为异步函数,它还不存在 但是您的代码根本不需要 top-level await?这就是为什么我想知道您是否接受了与问题中的问题无关的答案。 【参考方案1】:

不支持***await。标准委员会有一些讨论为什么会这样,例如this Github issue。

还有一个thinkpiece on Github 说明为什么***等待是一个坏主意。具体来说,他建议如果你有这样的代码:

// data.js
const data = await fetch( '/data.json' );
export default data;

现在,导入data.js任何文件在获取完成之前不会执行,因此您的所有模块加载现在都被阻止。这使得推理应用程序模块顺序变得非常困难,因为我们习惯于同步和可预测地执行*** javascript。如果允许这样做,知道函数何时被定义就变得很棘手。

我的观点是,您的模块仅通过加载就产生副作用是不好的做法。这意味着您的模块的任何使用者只需通过要求您的模块就会产生副作用。这严重限制了您的模块可以使用的地方。***await 可能意味着您正在读取某些 API 或在加载时调用某些服务。相反,您应该只导出消费者可以按照自己的节奏使用的异步函数。

【讨论】:

这是一个很好的链接,谢谢。可惜没有***支持。我希望是的。目前,我必须在这里嵌套我的承诺,这是非常糟糕的做法,我不喜欢它。 :(谢谢。 @SterlingArcher 或者,使用异步 IIFE:void async function() const inLobby = await isInLobby() () @robertklep 不会将inLobby 的范围限定为使其无法访问的函数吗? @SterlingArcher 是的,它会要求您将 all 代码移到那里(基本上使其成为“***”)。它只是使用 .then() 的替代方法(抱歉,应该更清楚一点)。 不同意,data.js 的用户在加载 data.js 本身及其所有依赖项时被“阻止”,这种“阻止”概念本身并不坏。***等待可以被认为是加载一些依赖项,在使用被“释放”之前似乎需要拥有这些依赖项。【参考方案2】:

当然总是这样:

(async () => 
    await ...

    // all of the script.... 

)();
// nothing else

这使您可以使用async 快速实现async 功能。它使您无需创建一个很棒的异步函数! //学分Silve2611

【讨论】:

请进一步详细说明并解释为什么会这样。 对这个答案投反对票是非常愚蠢的。它通过 async 实现了一个快速功能,您可以在其中使用 await。它使您无需创建一个很棒的异步函数! 不能解决问题,因为您仍然需要 await 这个匿名函数,这同样不能在函数外部工作。 那么这将如何处理错误情况?它也落在等待代码中吗? Tks 这是很大的帮助,特别是在获取登录 API 后回调承诺,这是完美响应以获取存储 getItem【参考方案3】:

您可以从 typescript 3.8 开始执行*** awaithttps://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html#-top-level-await 来自帖子: 这是因为以前在 JavaScript(以及具有类似功能的大多数其他语言)中,await 只允许在异步函数的主体中。但是,对于*** await,我们可以在模块的***使用 await。

const response = await fetch("...");
const greeting = await response.text();
console.log(greeting);

// Make sure we're a module
export ;

注意有一个微妙之处:*** await 仅在模块的***工作,并且文件仅在 TypeScript 找到导入或导出时才被视为模块。在某些基本情况下,您可能需要将 export 写为一些样板文件以确保这一点。

此时,***等待可能无法在您可能期望的所有环境中工作。目前,只能在目标编译器选项为 es2017 或以上,模块为 esnext 或 system 时使用*** await。多个环境和捆绑程序中的支持可能会受到限制,或者可能需要启用实验性支持。

【讨论】:

原生 JavaScript 也是如此。在节点中,您需要确保 "type": "module" 位于 package.json 的顶部, 使用 .jsm 文件扩展名。【参考方案4】:

从 Node.js 14.3.0 开始,使用标志支持***等待:

--experimental-top-level-await

从 Node.js 16.12.0 / 17.0.0 开始,不需要标志。

更多详情:https://v8.dev/features/top-level-await。

【讨论】:

以上是关于在异步函数之外使用 await的主要内容,如果未能解决你的问题,请参考以下文章

从生成器迁移到异步/等待

使用 async/ await 进行 异步 编程

如何在 catch 语句之外拒绝异步/等待?

使用 Await 读取异步对象集

我正在使用 asyncio,但异步函数正在使用 await asyncio.sleep(5) 阻塞其他异步函数

第126篇: 异步函数(async和await)