如何在顶层使用async/await?

Fel*_*ipe 117 javascript node.js async-await ecmascript-2017

我一直在浏览async/await,经过几篇文章后,我决定自己测试一下.但是,我似乎无法理解为什么这不起作用:

async function main() {  
    var value = await Promise.resolve('Hey there');
    console.log('inside: ' + value);
    return value;
}

var text = main();  
console.log('outside: ' + text);
Run Code Online (Sandbox Code Playgroud)

控制台输出以下内容(节点v8.6.0):

>外面:[对象承诺]

>里面:嘿那里

为什么函数内部的日志消息会在之后执行?我认为创建async/await的原因是为了使用异步任务执行同步执行.

有没有办法可以使用函数内部返回的值而不使用asyncafter await

T.J*_*der 167

我似乎无法理解为什么这不起作用.

因为main回报承诺; 所有async功能都有.

在顶层,您必须:

  1. 使用async永不拒绝的顶级函数,或

  2. 使用thencatch

#1 - await永不拒绝的顶级功能

(async () => {
    try {
        var text = await main();
        console.log(text);
    } catch (e) {
        // Deal with the fact the chain failed
    }
})();
Run Code Online (Sandbox Code Playgroud)

注意await; 你必须处理承诺拒绝/异步异常,因为没有别的东西; 你没有调用者将它们传递给.如果您愿意,可以通过async函数(而不是catch/ catchsyntax)调用它的结果:

(async () => {
    var text = await main();
    console.log(text);
})().catch(e => {
    // Deal with the fact the chain failed
});
Run Code Online (Sandbox Code Playgroud)

...这更简洁一些(因为这个原因,我喜欢它).

#2 - trycatch

main()
    .then(text => {
        console.log(text);
    })
    .catch(err => {
        // Deal with the fact the chain failed
    });
Run Code Online (Sandbox Code Playgroud)

then如果链中或catch处理程序中发生错误,将调用该处理程序.(确保您的catch处理程序不会抛出错误,因为没有注册任何错误来处理它们.)

或两个论点then:

main().then(
    text => {
        console.log(text);
    },
    err => {
        // Deal with the fact the chain failed
    }
);
Run Code Online (Sandbox Code Playgroud)

再次注意我们正在注册一个拒绝处理程序.但在这种形式中,请确保您的catch回调都没有抛出任何错误,没有注册任何错误来处理它们.

  • @Felipe:是的,`async`/`await` 是围绕 promise 的语法糖(很好的糖 :-))。你不只是*认为*它是在回报一个承诺;确实如此。([详情](https://tc39.github.io/ecma262/#sec-async-function-definitions-EvaluateBody)。) (3认同)
  • @Felipe-我已经更新了答案,因为顶级的“ await”提案已经达到了阶段3。:-) (2认同)

lau*_*gan 23

顶级await已移至第 3 阶段,因此您的问题的答案如何在顶级使用 async/await?只是将await调用添加到main()

async function main() {
    var value = await Promise.resolve('Hey there');
    console.log('inside: ' + value);
    return value;
}

var text = await main();  
console.log('outside: ' + text)
Run Code Online (Sandbox Code Playgroud)

要不就:

const text = await Promise.resolve('Hey there');
console.log('outside: ' + text)
Run Code Online (Sandbox Code Playgroud)

兼容性

  • @QuintenCabo 这可能是因为你没有使用 ES 模块。为了确保 Node 处于模块模式,请将 `"type": "module"` 添加到 `package.json` 中。 (2认同)

Ger*_*ica 10

要在当前答案之上提供更多信息:

node.js文件的内容当前以类似字符串的方式连接起来,形成一个函数体。

例如,如果您有一个文件test.js

// Amazing test file!
console.log('Test!');
Run Code Online (Sandbox Code Playgroud)

然后node.js将秘密连接一个如下所示的函数:

function(require, __dirname, ... perhaps more top-level properties) {
  // Amazing test file!
  console.log('Test!');
}
Run Code Online (Sandbox Code Playgroud)

需要注意的主要事情是生成的函数不是异步函数。所以你不能await直接在它里面使用这个词!

但是如果你需要在这个文件中使用 promises,那么有两种可能的方法:

  1. 不要await 直接在函数内部使用
  2. 不要使用 await

选项 1 要求我们创建一个新的作用域(这个作用域可以是async,因为我们可以控制它):

// Amazing test file!
// Create a new async function (a new scope) and immediately call it!
(async () => {
  await new Promise(...);
  console.log('Test!');
})();
Run Code Online (Sandbox Code Playgroud)

选项 2 要求我们使用面向对象的 promise API(使用 promise 的不太漂亮但功能相同的范例)

// Amazing test file!
// Create some sort of promise...
let myPromise = new Promise(...);

// Now use the object-oriented API
myPromise.then(() => console.log('Test!'));
Run Code Online (Sandbox Code Playgroud)

看到节点添加对顶级的支持会很有趣await

  • [Node 在 v13.3 中添加了对标记后面的顶级等待的支持]/sf/ask/4171005541/ #59585976)。 (3认同)

wob*_*ano 7

您现在可以在Node v13.3.0 中使用顶级 await

import axios from "axios";

const { data } = await axios.get("https://api.namefake.com/");
console.log(data);
Run Code Online (Sandbox Code Playgroud)

--harmony-top-level-await标志运行它

node --harmony-top-level-await index.js


Ton*_*gan 6

其他解决方案缺乏 POSIX 合规性的一些重要细节:

你需要 ...

  • 成功时报告 0 退出状态,失败时报告非零退出状态。
  • 向输出流发出错误stderr
#!/usr/bin/env node

async function main() {
 // ... await stuff ... 
}

// POSIX compliant apps should report an exit status
main()
  .then(() => {
    process.exit(0);
  })
  .catch(err => {
    console.error(err); // Writes to stderr
    process.exit(1);
  });
Run Code Online (Sandbox Code Playgroud)

如果您使用像Commander这样的命令行解析器,则可能不需要main().

例子:

#!/usr/bin/env node

import commander from 'commander'

const program = new commander.Command();

program
  .version("0.0.1")
  .command("some-cmd")
  .arguments("<my-arg1>")
  .action(async (arg1: string) => {
    // run some async action
  });

program.parseAsync(process.argv)
  .then(() => {
    process.exit(0)
  })
  .catch(err => {
    console.error(err.message || err);
    if (err.stack) console.error(err.stack);
    process.exit(1);
  });
Run Code Online (Sandbox Code Playgroud)


Duk*_*gal 5

这个问题的实际解决方案是以不同的方式处理它。

可能您的目标是某种初始化,通常发生在应用程序的顶层。

解决方案是确保在您的应用程序的顶层只有一个 JavaScript 语句。如果您的应用程序顶部只有一个语句,那么您可以在任何其他地方随意使用 async/await(当然要遵守正常的语法规则)

换句话说,将整个顶层包装在一个函数中,使其不再是顶层,这解决了如何在应用程序的顶层运行 async/await 的问题 - 你没有。

这是您的应用程序的顶层应该是这样的:

import {application} from './server'

application();
Run Code Online (Sandbox Code Playgroud)

  • 我不清楚抱歉。关键是通常在顶层,异步函数不会等待.... JavaScript 直接进入下一个语句,因此您无法确定您的 init 代码是否已完成。如果您的应用程序顶部只有一个语句,那这无关紧要。 (6认同)

Cha*_*kal 5

我喜欢这种巧妙的语法从入口点执行异步工作

void async function main() {
  await doSomeWork()
  await doMoreWork()
}()
Run Code Online (Sandbox Code Playgroud)


mik*_*ana 5

2021 答案:您现在可以在当前稳定版本的 node 中使用顶级 await

上面的大多数答案都有些过时或非常冗长,所以这里有一个节点 14 以后的快速示例。

制作一个名为 的文件runme.mjs

import * as util from "util";
import { exec as lameExec } from "child_process";
const exec = util.promisify(lameExec);
const log = console.log.bind(console);

// Top level await works now
const { stdout, stderr } = await exec("ls -la");
log("Output:\n", stdout);
log("\n\nErrors:\n", stderr);

Run Code Online (Sandbox Code Playgroud)

node runme.mjs

Output:
 total 20
drwxr-xr-x  2 mike mike 4096 Aug 12 12:05 .
drwxr-xr-x 30 mike mike 4096 Aug 12 11:05 ..
-rw-r--r--  1 mike mike  130 Aug 12 12:01 file.json
-rw-r--r--  1 mike mike  770 Aug 12 12:12 runme.mjs



Errors:
Run Code Online (Sandbox Code Playgroud)

  • 确保在 `package.json` 中使用 `"type": "module"`。 (3认同)
  • 我正在使用 Next.js,这对我很有帮助:/sf/answers/4783748161/ (2认同)
  • @RyanNorooz 在 `package.json` 中没有 `"type": "module"` 也是值得的 (2认同)

uin*_*tea 5

对于浏览器,您需要添加type="module"

没有type="module"

<script>
  const resp = await fetch('https://jsonplaceholder.typicode.com/users');
  const users = await resp.json();
  console.log(users)
</script>
Run Code Online (Sandbox Code Playgroud)

type="module"

<!--script type="module" src="await.js" -->
<script type="module">
  const resp = await fetch('https://jsonplaceholder.typicode.com/users');
  const users = await resp.json();
  console.log(users)
</script>
Run Code Online (Sandbox Code Playgroud)