【问题标题】:Give an array to bluebird, call an API, then integrating the results into a single JSON output给 bluebird 一个数组,调用一个 API,然后将结果整合到一个 JSON 输出中
【发布时间】:2016-06-16 07:31:22
【问题描述】:

序言

我有一个想法:如果我通过将我的对象串在一起作为微服务来减少我的重复数据结构,我为什么不使用 Sails、nodejs 和 api 调用。这就是一切的开始。

问题定义

如何将动态数组发送到函数,对另一个微服务执行未知数量的 API 调用,然后将返回的值组合到单个对象中进行处理?

旅程

我找到了关于 q 或 bluebird 的资料。

我有一个函数(在sails 中),它返回一个JSON 数组中的用户电子邮件地址列表。

getUserInfo: function(opt, callback){
    var https = require('https');
    var options = {
        hostname: opt.hostname || 'as.net.au',
        port: opt.port || 443,
        path: opt.path || '/developers',
        method: opt.method || 'GET',
        headers: opt.headers || {'Authorization': 'Basic ' + 'ThuperThecretKey'}
    }

    var req = https.request(options, function(res) {
        var data = '';
        res.setEncoding('utf8');
        res.on('data', function(chunk) {
            //console.log('BODY: ' + chunk); //oh so chunky
            data += chunk;
        });

        res.on('end', function() {
            //console.log(data)
            callback(null, data)
        })
    })

    req.on('error', function(e) {
        console.log('problem with request: ' + e.message)
        callback(e) //error
    })

    req.end()
}

它会返回一组 JSON 数组中的电子邮件地址:

['dev1@thisplace.com','dev2@thisplace.com','dev3@someotherplace.com'] 

然后我将它放入一个名为“devs”的 json 对象中。

所以现在我想将这个数组传递给一个函数,让它为每个开发者执行一个 API 调用,并将结果添加到 json 对象或新对象中。我假设一个新的 devs 是一个数组而不是一个完整的对象。

互联网上的共识似乎是使用bluebird,但我正在努力解决我假设每个人都在努力解决的问题,什么变量被命名为什么,更新了哪个,如何将东西加载到变量中,它去了哪里,等等等等,我的头疼。入口处有些东西,我不确定是否应该去那里。

所以我在寻求帮助。有没有人有一个例子说明异步执行这些操作然后解析结果的最佳方式?

我已经够远了,可以看到一个很好的异步调用循环发送到我的 API(如下所示),但是如何在最后一个 then 函数中传入和读取变量?

它记录了“为 dev1@thisplace.com 执行承诺.....等等” 然后“devs”对象为空,“dev”对象如预期(每个电子邮件地址)。

但是我如何将我的 API 调用的输出传递回调用 json(或新的 json)?它如何维护和异步更新对象? 我假设在错误之后(潜在的占位符编号 1)会在 else 中出现一些东西。

类似:

promise.map(devs, function(dev) {
  console.log('executing a promise for ' + dev)
  MyCont.getUserInfo({'path': '/developers' + dev}, function (err,devs) {
    if (err) {
      console.log(err)
    } else {
      console.log ('devs: ' + devs)
      console.log('dev: ' + dev)
      //Potential placeholder number 1 -Some here that loads the returned value to the passed in or new array?
    }   
  })
}).then(function() {
    //Or maybe it goes here?
    console.log("done");
});

【问题讨论】:

  • 那个问题非常有趣(+1 表示逗逗有趣的骨头)但我们真的不希望在这里写出这样的问题(-1 表示随后的编辑工作) .谢谢!

标签: arrays json node.js promise bluebird


【解决方案1】:

您提到了第一个显而易见的解决方案,即使用 Bluebird 的地图。首先是到处使用 Promise:

// make this a Promise
getUserInfo: function(opt) {
    // your new best friend for issuing http requests without callbacks
    var request = require('request-promise');

    // the options have to change a little
    var options = {
        uri: 'https://'
               + opt.hostname || 'as.net.au' + ':'
               + opt.port || 443
               + opt.path || '/developers',
        method: opt.method || 'GET',
        headers: opt.headers || {'Authorization': 'Basic ' + 'ThuperThecretKey'}
    }

    // much shorter, right?
    return request(options);
}

devs.map(function(dev) {
    console.log('executing a promise for ' + dev)
    return MyCont.getUserInfo({'path': '/developers' + dev});
}).then(function(results) {
    // your results are here, in order
    console.log(results);
});

【讨论】:

  • 哇,谢谢你,这是一个非常简短的回答。正是我所需要的。
【解决方案2】:

在阅读完所有内容后,我假设您的问题是:

如何使用一堆异步函数调用的结果来解决现有的承诺?

首先,从你最外层promise中的函数开始:

var processEmailAddressList = function (addresses) {
    // This is inside a promise.
    // It will return when all addresses have been processed.
    return Promise.map(addresses, processSingleAddress);
};

var processSingleAddress = function (address) {
    // How you implement this is up to you -- and up to whatever
    // promise library you are using. return a promise that resolves
    // to the value you want
};

在这里,我们使用map,但我们可以使用reducemapreduce 之间有一些非常重要的性能特征(见下文),但我现在将忽略它。您只需要知道,当addresses 中的所有项目都已由回调processSingleAddress 处理后,您将返回一个承诺resolvereject

问题是processSingleAddress怎么写。

如果您可以使用已经使用 Promise 的方式处理该单个地址,那么这很简单。也许你正在使用一个已经准备好 Promise 的库。在这种情况下,您只需直接调用该函数即可。

var processSingleAddress = function (address) {
    return addressProcessorWithPromise(address);
};

但是,如果您无法使用 Promise 处理您的列表项,您需要找到一种方法来使用您自己创建的 Promise。例如,在这里,我假设您正在对 getDataFromAsyncProcess() 进行异步调用,并且该函数采用单个地址和节点样式的回调。

var processSingleAddress = function (address) {
    // input is `sample@example.com`
    return new Promise(function (resolve, reject) {
        getDataFromAsyncProcess(address, function (err, data) {
            if (err) reject(err);

            // example output is {address: 'sample@example.com', status: 'logged in'}
            resolve(data);
        });
    });
};

此时,您应该期望事情会像这样运行:

var addresses = ['sample1@example.com', 'sample2@example.com']
processEmailAddressList(addresses)
.then(function (list) {
    // see below for the value of `list`
})
.catch(function (err) {
    console.error(err);
});

then 内,列表现在应该如下所示:

[
    {address: 'sample1@example.com', status: 'logged in'},
    {address: 'samples@example.com', status: 'logged in'},
]

我知道这看起来可能与正在发生的间接数量(函数内部的函数)混淆,但欢迎使用 JS。而且你越习惯使用 Promise,它就越容易。

最后要真正小心的是mapreduce(或任何其他promise-library 迭代器)如何做事。如果你有 5 个地址并且进程不是那么昂贵,这不是那么关键,但是如果你有成百上千的事情要处理或者处理器很昂贵(数据库调用、网络请求、内存消耗等),如果所有这些地址都是并行处理的(一次全部),您最终可能会消耗大量内存并杀死您的数据库。如果这是一个因素,您应该尝试使用一次只做一件事情的迭代器。有关详细信息,请参阅您的库 API。

最后一点:请尽量让您的问题保持简单。那是不必要的冗长。

【讨论】:

  • 感谢您抽出宝贵时间详细回答这个问题。它的信息量很大。 (保持简短)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-12-21
  • 2014-07-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多