异步功能与等待

use*_*885 3 javascript ajax asynchronous node.js async-await

我来自PHP背景,我正在尝试学习NodeJS.

我知道Node中的所有内容都是异步的,但我发现我在代码中一直使用async/await组合,并且我想确保我没有做一些愚蠢的事情.

原因是我有很多情况需要在继续之前得到某些结果(例如,对于一个小的ajax请求).这个小的ajax请求没有别的目的,除了做我想要它的东西以及我指定的顺序,所以即使我做"异步方式"并使用回调写它我仍然有等待事情以正确的顺序完成.

现在每当我发现自己处于这种情况时,我只是等待等待结果:

即:

var result = await this.doSomething(data);
Run Code Online (Sandbox Code Playgroud)

反对使用回调

this.doSomething(data, function(callback) {
  // code
  callback();
});
Run Code Online (Sandbox Code Playgroud)

对我来说,第一个例子比第二个例子更清晰,这就是为什么我一直在选择它.但是我担心我可能会遗漏一些基本的东西.但是在异步调用下没有其他任何东西要处理的情况下,事情进展的唯一方法就是它遵循同步风格,在第二种风格中使用第一种风格有什么问题吗?

T.J*_*der 6

但是我担心我可能会遗漏一些基本的东西.

不,你不是,这正是你想要做的,假设this.doSomething(data)是异步的(如果它是一个ajax调用,人们希望它是异步的)并且它返回一个promise(所有用async关键字定义的函数都是隐含的).(如果它不是异步的,你不需要它await,尽管它是允许的.)

事情正在经历一个过渡,你可能有点困惑(可以理解).直到最近,Node API(内置的和第三方模块提供的)的压倒性惯例是使用"节点回调"模式,这是一个将进行异步工作的函数,它的最后一个参数是一个回调函数,它将调用第一个参数指示成功/失败(null=成功,其他任何东西是一个错误对象),后续参数提供结果.(你的第二个例子假设doSomething是其中之一,而不是一个返回一个promise的函数.)

示例:fs.readFile,您使用如下:

fs.readFile("/some/file", "utf-8", function(err, data) {
    if (err) {
        // ...handle the fact an error occurred..
        return;
    }
    // ...use the data...
});
Run Code Online (Sandbox Code Playgroud)

然而,这种风格很快导致回调地狱,这是承诺(又名"期货")被发明的原因之一.

但是很多 Node API仍然使用旧模式.

如果需要使用"节点回调" - 模式函数,则可以使用util.promisify它们创建启用许可的版本.例如,假设您需要使用fs.readFile,它使用Node回调模式.您可以获得这样的承诺版本:

const readFilePromise = util.promisify(fs.readFile);
Run Code Online (Sandbox Code Playgroud)

...然后将其与async/ awaitsyntax一起使用(或直接使用promise then):

const data = await readFilePromise("/some/file", "utf-8");
Run Code Online (Sandbox Code Playgroud)

还有一个npm名为的模块promisify可以提供整个API的承诺版本.(可能不止一个.)

仅仅因为在大多数情况下promises和async/ await替换旧的Node回调样式并不意味着回调仍然没有位置:Promise只能解决一次.他们是一次性的事情.所以回调仍然有一个地方,比如使用EventEmitterson方法,就像一个可读的流:

fs.createReadStream("/some/file", "utf-8")
    .on("data", chunk => {
        // ...do something with the chunk of data...
    })
    .on("end", () => {
        // ...do something with the fact the end of the stream was reached...
    });
Run Code Online (Sandbox Code Playgroud)

由于data会多次触发,因此使用回调是有意义的; 承诺不适用.

另请注意,您只能awaitasync函数中使用.因此,您可能会发现自己习惯于"主要"模块结构,看起来像这样:

// ...Setup (`require` calls, `import` once it's supported, etc.)...
(async () => {
    // Code that can use `await `here...
})().catch(err => {
    // Handle the fact an error/promise rejection occurred in the top level of your code
});
Run Code Online (Sandbox Code Playgroud)

catch如果您希望脚本以未处理的错误/拒绝终止,您可以将其关闭.(Node还没有这样做,但是一旦未处理的拒绝检测成熟了.)

或者,如果要在非async函数中使用启用promise的函数,只需使用thencatch:

this.doSomething()
    .then(result => {
        // Use result
    })
    .catch(err => {
        // Handle error
    });
Run Code Online (Sandbox Code Playgroud)

注意:节点7.x及更高版本中直接支持async/ await.如果您打算使用async/,请确保您的目标生产环境支持Node 7.x或更高版本await.如果没有,您可以使用像Babel这样的东西来转换代码,然后在旧版本的Node上使用转换后的结果.

  • 承诺仅适用于曾经发生一次的事件.回调应该用于多次出现的回调. (2认同)