异步功能与等待

时间:2017-11-10 12:47:21

标签: javascript node.js ajax asynchronous async-await

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

我知道Node中的所有内容都是异步的,但我发现我的代码中已经使用了异步/等待组合,并且我想确保我不做有点蠢。

原因是我有很多情况需要需要某些事情才能继续(例如对于一个小的ajax请求)。这个小的ajax请求没有其他目的,除了做我想要它的东西以及我指定的顺序,所以即使我做了事情"异步方式"并使用回调来编写它,我仍然需要等待以正确的顺序完成任务。

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

即:

var result = await this.doSomething(data);

反对使用回调

this.doSomething(data, function(callback) {
  // code
  callback();
});

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

1 个答案:

答案 0 :(得分:6)

  

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

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

事情正在经历转型,你可能会感到有点困惑(可以理解)。直到最近,Node API(内置的和第三方模块提供的)的压倒性惯例是使用" Node回调"模式,这是一个将进行异步工作的函数,它的最后一个参数是一个回调函数,它将调用第一个参数来指示成功/失败(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...
});

这种风格很快就会导致回调地狱,这也是承诺(又名" future")被发明的原因之一。

很多的Node API仍然使用旧模式。

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

const readFilePromise = util.promisify(fs.readFile);

...然后将其与async / await语法一起使用(或直接通过then使用承诺):

const data = await readFilePromise("/some/file", "utf-8");

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

仅仅因为承诺和async / await在大多数情况下取代旧的Node回调风格并不意味着回调不会占有一席之地:承诺只能解决的一次即可。他们是一次性的事情。所以回调仍然有一个位置,例如使用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...
    });

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

另请注意,您只能在await功能中使用async。因此,你可能会发现自己养成了一个主要的习惯。模块结构看起来像这样:

// ...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
});

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

或者,如果您想在非async函数中使用启用承诺的函数,只需使用thencatch

this.doSomething()
    .then(result => {
        // Use result
    })
    .catch(err => {
        // Handle error
    });

注意:节点7.x及更高版本中直接支持async / await。如果您要使用async / await,请确保您的目标制作环境支持Node 7.x或更高版本。如果没有,您可以使用Babel之类的内容来转换代码,然后在旧版本的Node上使用转换后的结果。