用瀑布填充物体的正确方法

时间:2017-10-11 23:18:28

标签: node.js

我的节点js模块中有一个以下对象,名为data:

{
    "item_uuid": "77306c44-4175-4aee-866d-d8df89fa3ii9",
    "accounts": [{
        "accountid": "B15501",
        "quantity": 1
    },
    {
        "accountid": "S20000",
        "quantity": 1
    }]
}

我需要在传递整个数据以供进一步处理之前,通过将accountid传递给API来填充帐户中的每个帐户和国家/地区代码。

所以我循环帐户中的每个帐户并执行以下操作:

data.accounts.forEach(function(account) {

    var clientAPI = "http://0.0.0.0:3000/" + account.accountid + "/?fields=country";
    request.get(clientAPI, function (err, response, body) {
        if (err) {
            console.log("Unable to get country code for " +
                    account.accountid + " : " + err.message);
        } else {
            var clientData = JSON.parse(body);
            account.country_code = clientData.country
        }
    })
}

// once all accounts have got country code, perform insertion into database
processData(data);

不幸的是,对clientAPI的调用是异步的,它不会等待返回结果,因此当数据到达processData时,它仍然没有country_code。

所以我在这里尝试瀑布:

var waterfall = require('async-waterfall');

waterfall([
    function (callback) {
        data.accounts.forEach(function(account) {

            var clientAPI = "http://0.0.0.0:3000/" + account.accountid + "/?fields=country";
            request.get(clientAPI, function (err, response, body) {
                if (err) {
                    console.log("Unable to get country code for " + account.accountid + " : " + err.message);
                } else {
                    var clientData = JSON.parse(body);
                    account.country_code = clientData.country
                }
            })
        }

        callback(null, data);
    }
], function(err, data){
    processData(data);          
})

太糟糕了,它仍然不起作用,当涉及到processData(数据)时,它仍然没有country_code。

在这里使用瀑布,我错过了什么?除了在processData(数据)之前填充country_code,我还能做些什么呢?

我必须调用API来获取每个帐户的country_code。

2 个答案:

答案 0 :(得分:1)

尝试promises(也许是节点7.6+中新的await / async语法),它们会产生更清晰,更符合逻辑的代码流。

const Promise = require('bluebird')
const request = Promise.promisifyAll(require('request'))

function getData(data){
  return Promise.each(data.accounts, function(account){
    const clientAPI = {
      method: 'GET',
      json: true,
      uri: `http://0.0.0.0:3000/${account.accountid}/?fields=country`,
    }
    return request.getAsync(clientAPI).then(function(response){
      if (!response.body.country) throw new Error('No country on '+account.accountid')
      account.country_code = response.body.country
    })
    .catch(function(err){
      console.error('Unable to get country code for '+account.accountid+' : '+err.message, err);
    })
  })
}

getData(data).then(function(){ processData(data) })

你也可以使用Promise.map一些并发而不是串行的Promise.each,并等待每个请求完成后再转移到下一个请求。

不捕获错误(或重新抛出err)将导致Promise被拒绝而不是仅仅记录,这可能对程序更有用。 Promises的好处是错误会冒出来,所以你可以在程序/调用开始时以更全局的方式处理错误。

答案 1 :(得分:0)

如果您使用promises,async / await或生成器语法,这将更好地实现。

使用回调功能,使用计数器变量来跟踪您发送的请求数量,并在致电processData之前等待计数器达到某个值。

let counter = 0;

data.accounts.forEach(function(account) {

var clientAPI = "http://0.0.0.0:3000/" + account.accountid + "/?fields=country";
request.get(clientAPI, function (err, response, body) {
    if (err) {
        console.log("Unable to get country code for " +
                account.accountid + " : " + err.message);
        counter++
    } else {
        var clientData = JSON.parse(body);
        account.country_code = clientData.country
        counter++
    }
})
}

// once all accounts have got country code, perform insertion into database
if (counter === data.accounts.length) processData(data);