在异步函数外使用await


85

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

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

这是我的代码的混帐摘要(您可以在此处看到完整的范围),它只是检查玩家是否已经在大厅中,但这无关紧要。

接下来,我们有这个异步功能。

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

如果,则无需运行此功能exit === true

我试着做

const inLobby = await isInLobby();

我希望可以等待结果,以便可以inLobby有条件地运行countPlayer,但是我收到了没有具体细节的typeerror。

为什么您不能awaitasync功能超出功能范围?我知道这是一个糖承诺,因此必须将其链接到其中,then但是为什么countPlayer我可以等待另一个诺言,但是在外面,我却不能await isInLobby呢?


你能告诉我们哪里你做await isInLobby(),以及如何inLobby使用?另外,在哪里/如何countPlayer称呼?
Bergi

@Bergi我将回购链接到实际环境。太多的代码投入的问题
英镑弓箭手

我看不出问题出在哪里(也许您已经更新了仓库)?如果您指的是isInLobby().then( … countPlayer().then …零件,则解决方案是微不足道的:只需创建包含这些调用的函数((req, res) =>一个)即可async
Bergi

@Bergi的问题不是它坏了,而是按原样工作。我只是不明白为什么顶级等待不是什么。事实证明,如果不将整个模块作为异步功能进行扩展,它就不存在了
Sterling Archer

但是,您甚至根本不需要顶级 await代码?这就是为什么我想知道您接受的答案与问题中的问题并不真正相关。
Bergi

Answers:


73

await不支持顶层。标准委员会对此进行了一些讨论,例如Github问题

在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

@robertklep不会使inLobby该功能无法访问吗?
Sterling Archer

@SterlingArcher是的,它将需要您将所有代码移到那里(基本上使其成为“顶层”)。这只是使用的替代方法.then()(对不起,应该更清楚一点)。
robertklep

不同意,尽管data.js的用户在加载data.js本身及其所有依赖项时仍被“阻止”,但这种“阻止”的概念本身并不坏。顶层等待可以被认为是加载了一些依赖,在使用被“释放”之前,这些依赖似乎已经存在。
易卜拉欣·本·萨拉赫

125

当然总会有这样:

(async () => {
    await ...

    // all of the script.... 

})();
// nothing else

这使您可以使用async快速实现异步功能。它省去了制作一个很棒的异步函数的需要!//信用Silve2611


3
请进一步详细说明并解释其工作原理。
Paul Back

12
Downvote这个答案是非常愚蠢的。它具有异步的快速功能,您可以在其中使用await。它省去了制作一个很棒的异步函数的需要!
Silve2611'9

55
无法解决问题,因为您仍然需要await此匿名函数,而该匿名函数同样无法在函数外部运行。
迈克尔

那么这将如何处理错误情况?它也落在等待代码中吗?
曼努埃尔·埃尔南德斯

这是个很大的帮助,特别是一旦获取登录API即可回调承诺,可以完美地响应以获取存储getItem
tess hsu 19/11/22

9

更好的方法是在代码块之前添加其他分号

;(async () => {
    await ...
})();

这样可以防止自动格式化程序(例如,在vscode中)将第一个括号移动到前一行的末尾。

该问题可以在以下示例中得到证明:

const add = x => y => x+y
const increment = add(1)
(async () => {
    await ...
})();

如果没有分号,它将被重新格式化为:

const add = x => y => x+y
const increment = add(1)(async () => {
  await Promise(1)
})()

这显然是错误的,因为它将异步函数分配为y参数并尝试从结果中调用函数(实际上是一个奇怪的字符串'1async () => {...}'


20
错误的不是重新格式化。没有分号,这就是在运行时解析函数的方式,并且会出现错误,换行或没有换行。您的示例中的正确解决方案是在add(1);异步函数之后而不是之前添加分号。
Madara's Ghost

11
而且,老实说,我不明白这与眼前的问题有何关系。
马达拉的幽灵

你是对的。另外,运行时需要分号。
Viliam Simko '18


1

自打字稿3.8开始,您就可以进行顶级等候
。https
: //www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html#-top-level-await帖子:
这是因为以前在JavaScript(以及其他大多数具有类似功能的语言)仅在异步函数的主体内允许等待。但是,通过顶层等待,我们可以在模块的顶层使用等待。

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

// Make sure we're a module
export {};

请注意,这有一个微妙之处:顶层等待仅在模块的顶层起作用,并且只有当TypeScript找到导入或导出时,文件才被视为模块。在某些基本情况下,您可能需要写出export {}作为样板,以确保这一点。

此时,顶层等待可能并不适用于您可能期望的所有环境。当前,仅当目标编译器选项为es2017或更高版本且模块为esnext或system时,才可以使用顶级等待。几种环境和捆绑软件中的支持可能会受到限制,或者可能需要启用实验性支持。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.