如何同步节点中的两个异步调用?

Posted

技术标签:

【中文标题】如何同步节点中的两个异步调用?【英文标题】:How to synchronize two asynchronous calls in node? 【发布时间】:2018-06-24 20:30:37 【问题描述】:

我使用 Python 和 javascript(在浏览器中)开发(业余级)。我逐渐接受并喜欢 JS 的异步特性,再加上它创造了奇迹的反应式框架。

我现在尝试使用 NodeJS 来替换我服务器上的 Python 脚本。该程序的一般流程是获取(HTTP)一些 API,一旦我拥有了所有这些 API,就可以做一些事情。这非常适合 Python,我只是连续调用并收集结果。性能和时间无关紧要。

虽然 NodeJS 文档讨论了 blocking vs non-blocking code,但在我看来,浏览器中 JavaScript 的异步特性在 NodeJS 中非常普遍。具体来说,在我的情况下,fetch 到节点的端口是基于 Promises 的,需要通过箍来使此类调用阻塞。

我应该如何同步我的调用以最终对所有收集的结果采取行动?我的代码类似于

fetch(urlOne)
  .then(res => res.json())
  .then(res => a = res.a)

fetch(urlTwo)
  .then(res => res.json())
  .then(res => b = res.b)

// here comes the moment when both a and b are to be used

我可以将一个 fetch 与另一个链接(在第一个 .then() 中),但这会分散脚本的主要机制:“get a, get b 和做一些事情他们”)。 具体来说,是否有类似 Python 的 join() 之类的东西等待线程结束(阻塞主线程)?


请注意,我理解并欣赏浏览器中 JavaScript 的异步方法。有一个输出(渲染的 DOM)感觉很自然,当它们可用时,它会用一些元素异步更新。这对于 Web 服务器等后端服务也很有用。不过,就我而言,活动是非常线性的(或者——这是我问题的核心——需要在某个时候同步)

【问题讨论】:

Promise.all() 是您知道何时完成多个承诺的方式。 查看bluebirdjs 框架中有大量有用的 promise 助手。 【参考方案1】:

这样做的正确方法确实是使用Promise.all,但不需要有副作用的then 调用(写入回调关闭的变量)。 all 以数组的形式(与调用顺序相同)提供结果作为其分辨率值:

Promise.all([
    fetch(urlOne)
      .then(res => res.json())
      .then(res => res.a) // <== No `a =` here
    ,
    fetch(urlTwo)
      .then(res => res.json())
      .then(res => res.b) // <== No `b =` here
]).then(([a, b]) =>      // <== Destructured parameter picking out the first
                          //     and second entries of the array argument
    // here comes the moment when both a and b are to be used
);

fetch 的替代品示例:

// The `fetch` stand-in
function fetch(url) 
  return new Promise(resolve => 
    setTimeout(() => 
      resolve(
        json: () => new Promise(resolve => 
          setTimeout(() => 
            resolve(a: "a:" + url, b: "b:" + url);
          , url === "urlOne" ? 200 : 100);
        )
      );
    , 100);
  );

// End of stand-in

Promise.all([
    fetch("urlOne")
      .then(res => res.json())
      .then(res => res.a)
    ,
    fetch("urlTwo")
      .then(res => res.json())
      .then(res => res.b)
]).then(([a, b]) => 
    console.log(`a = $a, b = $b`);
);

【讨论】:

【参考方案2】:

您可以使用Promise.all() 来等待多个异步函数。

let firstAsync = fetch(urlOne)
                 .then(res => res.json())
                 .then(res => res.a)

let secondAsync = fetch(urlTwo)
                  .then(res => res.json())
                  .then(res => res.b)

Promise.all([firstAsync, secondAsync]).then(() => 
  // here comes the moment when both a and b are to be used
)

【讨论】:

这个答案已经在meta上提到了。【参考方案3】:

你可以使用Bluebird.props方法。

const Bluebird = require('bluebird');

var allResponses = 
    a: fetch(urlOne)
    b: fetch(urlTwo)
;

Bluebird.props(allResponses)
    .then(all => 
        console.log(all.a);
        console.log(all.b);
);

PS:Bluebird 与 Promises/A+ specs 兼容。这意味着您可以安全地使用它,或者在 Promise 类中构建它。

我通常在我的项目中用 Bluebird 覆盖 Promise 类。

global.Promise = require('bluebird'); 

【讨论】:

有趣。你甚至可以在最后加入解构:.then((a, b) =&gt; /* use a and b here */);【参考方案4】:

您可以简单地使用 async npm packege 处理大量内容。它可以并行或同时运行您的函数,当所有函数完成后,将返回包含所有结果的最终回调。

【讨论】:

以上是关于如何同步节点中的两个异步调用?的主要内容,如果未能解决你的问题,请参考以下文章

如何在同步函数中等待 JavaScript 中的异步调用?

同步和异步的概念

Javascript - 异步调用后同步

在asp.net里怎样异步调用WebService方法

如何将节点中的异步回调添加到函数调用?

Gevent中的同步与异步详解