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

时间:2018-01-16 07:30:28

标签: node.js multithreading asynchronous synchronization thread-synchronization

我用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 {{ 1}}和他们做点什么“)。 具体来说,有没有像Python的join()那样等待线程结束(阻塞主线程)?

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

4 个答案:

答案 0 :(得分: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
)

答案 1 :(得分:2)

执行此操作的正确方法确实是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 :(得分:1)

您可以使用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'); 

答案 3 :(得分:0)

只需使用async npm packege即可。它可以并行或同时运行您的函数,当所有函数完成后,将返回包含所有结果的最终回调。