结合多个Node.js API调用的结果

时间:2017-06-20 18:55:05

标签: javascript node.js

这里是Node.js的新手。我正在寻找从另一个函数中进行N个异步API调用的正确方法,并将它们的结果组合起来以便进一步使用下游。在我的情况下,N会相当小并且阻止它们的执行也不会太糟糕。

在同步执行中,下面的combine()的实现应该有效。

如果我只需要一次API调用的结果,那么在提供给callAPI()的回调函数中实现以下逻辑将是直截了当的。我绊倒的地方是在执行foo之前我需要所有结果的时候(总计,[... args])。

我调查了async.whilst,但无法让它发挥作用。如果这实际上符合我的需要,我会持怀疑态度。我也调查了Promises这似乎是正确的领导,但在爬进那个巨大的兔子洞之前获得再保证会很好。是Promises是正确的方法,哪个模块是Node.js项目中使用的标准?

var http = require('http');

function callAPI(id) {
    var options = {
        host: 'example.com',
        path: '/q/result/'.concat(id)
    }

    var req = http.get(options, (res) => {
        var body = [];
        res.on('data', (chunk) => {
            body.push(chunk);
        }).on('end', () => {
            body = Buffer.concat(body).toString();
            return body;
        }).on('error', (err) => {
            console.error(err);
        });
    });
}

function combine(inputs) {

    var total = 0;
    for (i=0; i < inputs.length; i++) {
        total += callAPI(inputs[i]['id']);
    };
    console.log(total);

    // call some function, foo(total, [...args])
}

编辑1:

我尝试按照下面的samanime的回答修改API调用以返回Promise。参见:

function callAPI(id) {
    return Promise((resolve, reject) => {
        var options = {
            host: 'example.com',
            path: '/q/result/'.concat(id)
        }

        var req = http.get(options, (res) => {
            var body = [];
            res.on('data', (chunk) => {
                body.push(chunk);
            }).on('end', () => {
                body = Buffer.concat(body).toString();
                resolve(body);
            }).on('error', (err) => {
                reject(err);
            });
        });
    });
}

function combine(inputs) {

    var combined = [];
    for (i=0; i < inputs.length; i++) {
        total += callAPI(inputs[i]['id']);
            .then(result => {
                combined.push(result);
            });
    };
    var total = combined.reduce((a, b) => a + b, 0);
    console.log(total);

    // call some function, foo(total, [...args])
}

这似乎让我半途而废。如果我在console.log(combined)块中then(),我可以看到列表中包含来自API调用的结果。但是,我仍然无法在&#34; end&#34;访问完整的combinedfor循环。在构建完整列表后,我可以将回调附加到要运行的内容吗?还有更好的方法吗?

编辑2(我的解决方案 - 根据Patrick Roberts的建议)

function callAPI(id) {
    return Promise((resolve, reject) => {
        var options = {
            host: 'example.com',
            path: '/q/result/'.concat(id)
        }

        var req = http.get(options, (res) => {
            var body = [];
            res.on('data', (chunk) => {
                body.push(chunk);
            }).on('end', () => {
                body = parseInt(Buffer.concat(body));
                resolve(body);
            }).on('error', (err) => {
                reject(err);
            });
        });
    });
}

function combine(inputs) {
    var combined = [];
    Promise.all(inputs.map(input => callAPI(input.id)))
        .then((combined) => {
            var total = combined.reduce((a, b) => a + b, 0);
            // foo(total, [...args])
        });
};

3 个答案:

答案 0 :(得分:2)

听起来你可以将一堆承诺链接在一起,传递数据。

基本上类似于:

const combined = [];
asyncOne()
  .then(result => { combined.push(result); return asyncTwo())
  .then(result => { combined.push(result); return asyncThree())
  // and so on

只要每个函数都返回一个promise,你就可以全部设置。

如果您想并行运行它们,请使用Promise.all(),这将为您做同样的事情:

Promise.all([asyncOne(), asyncTwo(), asyncThree() /* , etc */])
  .then(combined => /* combined is an array with the results of each */)

到目前为止,这是此类事情的首选模式。

答案 1 :(得分:0)

你的编辑看起来好多了,但试试这个:

function callAPI(id) {
  return Promise((resolve, reject) => {
    var options = {
      host: 'example.com',
      path: '/q/result/' + id
    }

    http.get(options, (res) => {
      var body = [];
      res.on('data', (chunk) => {
        body.push(chunk);
      }).on('end', () => {
        body = Buffer.concat(body).toString();
        resolve(body);
      }).on('error', reject);
    });
  });
}

function combine(inputs) {
  Promise.all(inputs.map(input => callAPI(input.id))).then((combined) => {
    // completed array of bodies
    console.log(combined);
    // foo(combined.length, [...args]);
  }).catch((error) => {
    console.log(error);
  });
}

答案 2 :(得分:-1)

我会添加一个计数器来跟踪剩余的API调用。每当API调用完成,减少,如果它为0,你就完成了。

&#13;
&#13;
const numCalls = 10;
let remaining = numCalls;
let data = [];

function getRandomInt(min, max) {
  min = Math.ceil(min);
  max = Math.floor(max);
  return Math.floor(Math.random() * (max - min)) + min;
}

function ajax() {
    // Simulate ajax with a setTimeout for random amount of time.
    setTimeout(() => {
        // This is the callback when calling http.get
        data.push(getRandomInt(0, 10)); // Some data from server
        if (--remaining <= 0) {
            // Am I the last call? Use data.
            console.log(data);
            console.log(data.length);
        }
    }, getRandomInt(1000, 3000));
}

for (let i = 0; i < numCalls; i++) {
    ajax();
}
&#13;
&#13;
&#13;