【问题标题】:Q.all array of request promises, not sure how to get the resultsQ.all 请求承诺数组,不知道如何得到结果
【发布时间】:2016-01-03 02:02:54
【问题描述】:

这可能是一个明显的答案,但我不确定采取什么方法。 请求是一个节点模块:https://github.com/request/request 我填充了一组 getHistory 请求(使用不同的参数)。 p = [p1,p2...]。

this.app.all('/api/heatmap', function(req,res) {
   // fill p here _.each(blabla, p.push(gethistory(someparams...)
   var result = [];
   function getHistory(params) {
        var options = { ...};
        var callback = function(error, response, body) {
            if(error) { //wtv 
            } else {
              // what to do with the body here ? return body ? result.push(body) ?
            }
        }

        request(options, callback);
    }

    Q.all(p).then(function() {
    });
}

所以这里的问题是我在完成所有请求时,将所有内容放在一个数组/对象中,然后将整个内容发送给客户端。如何让 getHistory 返回获取的值(请求完成后)。

希望清楚。

【问题讨论】:

标签: node.js promise q


【解决方案1】:

这里的核心问题是 node.js 风格的回调和 Promise 不兼容。 Promise 强调返回值,node 强调回调。

因此,您需要一种能够正确包装节点回调约定的适配器,称为Promisifcation 的过程。这可以手动完成,但如果您不小心,它充其量是乏味的并且容易出错。幸运的是,由于节点的约定是完善的,它可以自动化。 Q 对此有a few helpers,但Bluebird 在这方面要方便得多。

因此,简单的方法是切换到 Bluebird 作为 Promise 库并使用 promisifyAll

var Promise = require('bluebird');
var request = Promise.promisifyAll(require("request"));

this.app.all('/api/heatmap', function(req, res) {
    var requests = blabla.map(function (item) {
        return request.getAsync({ /* params */ });
    });

    Promise.all(requests).then(function (responses) {
        res.send( JSON.stringify(responses) ); // whatever
    }).catch(function (error) {
        res.send( "An error ocurred: " + error ); // handle error
    });
}

【讨论】:

  • 不确定你的意思,但我找到了我需要的功能,它似乎更简单。 Q模块想到了这个问题。检查我的答案
  • 不确定“不确定您的意思”是什么意思。我的回答很清楚。但是恭喜你,正如我所说,您只是手动承诺了请求模块的一部分。它可能看起来更简单,但那是因为你想以这种方式感知它,而不是因为它实际上是。
  • 根据Q doc,这个问题“这里的核心问题是node.js风格的回调和promise不兼容。”使用延迟功能解决。我已经阅读了关于 promification 的部分,这真的很有趣,谢谢。但是使用 promisifcation 方法不是更重/更不透明吗?如果不是,这是否意味着我应该使用 bluebird 而不是 Q(因为 promisification 是一个更高级的功能?)?
  • 我回答的第一段说的完全一样。是的,可以手动完成,你做到了。但是由于request 不是唯一使用节点样式回调约定的节点函数,因此您将不得不重复多次,这很快就会变得乏味(并且容易出错)。我的回答也是这么说的。我开始有一种下沉的感觉,你完全跳过了阅读文本。 :-/
  • 也就是说,promisification 并不是为特殊情况保留的“高级功能”。毕竟是你自己做的。它与 Bluebird 无关 - Bluebird 仅提供一种可靠地自动化该过程的方法。此外,您使用 request 模块而不是自己实现 HTTP 规范,为什么不将其视为“更重/更不透明”?
【解决方案2】:

FWIW,这是另一个答案,它显示了使用 Q 正确完成时的外观:

// promisified request
function requestAsync(options) {
    var result = Q.defer();
    request(options, function(error, response, body) {
        if (error) {
            result.reject(error);
        } else {
            result.resolve(body);
        }
    });
    return result.promise;
}

// returns promises for heatmapVolumes
function getHistory(params) {
    return requestAsync({
        method: 'GET',
        url: 'https://api.kaiko.com/v1/charts/' + 
            encodeURIComponent(params.exchange) + '/' + 
            encodeURIComponent(params.pair) +'/history',
        qs: params.qs,
        headers: {
            "Content-Type": "application/json",
            "Accept": "application/json"
        }
    }).then(function (body) {
        return heatmapVolume(body, params.exchange, params.pair);
    }).catch(function (error) {
        // log detailed error and send less revealing message downstream
        console.error('error fetching trades', error);
        throw new Error('Something went wrong while fetching trades');
    });
}

// usage
this.app.all('/api/heatmap', function(req, res) {
    getHistory({
        exchange: "foo", pair: "bar", qs: "qux"
    }).then(function (heatmap) {
        res.send(200, heatmap);
    }).catch(function (error) {
        res.send(500, error);
    });
});

【讨论】:

  • Q 有 nfcallnfbind 助手 - 即使在像 Q 这样的较旧的劣质库中,您仍然可以并且应该进行自动承诺
  • 当然。但是,您仍然必须在每个功能的基础上执行此操作,因此它仍然是单调乏味的。这个答案是 OP 的答案的改进版本,做他所做的,减去明显的错误(不确定是否有任何非明显的错误,请查看)。我不想将它作为一个 pastebin 链接发布到 OP 答案的 cmets 中。
  • 这会像 Q 一样失败,done 的返回值是 undefined,它用于表示承诺链的绝对结束。
  • 来自 jQuery 的肌肉记忆。固定的。谢谢。
【解决方案3】:

使用 Q.deferred 并且它按照文档说明工作 \o/

function getHistory(params) {
                var deferred = Q.defer();
                var options = {
                    method: 'GET',
                    url: 'https://api.kaiko.com/v1/charts/' + params.exchange + '/' + params.pair +'/history',
                    qs:qs,
                    headers: {
                        "Content-Type": "application/json",
                        "Accept": "application/json"
                    }
                }

                var callback = function(error, response, body) {
                    if(error) {
                        console.log('error fetching trades', error);
                        res.send(500, 'Something went wrong while fetching trades');
                    } else {
                        var body = heatmapVolume(body, params.exchange, params.pair);
                        // console.log("result!", body.exchange, body.pair);
                        result.push(body);
                        // return body;
                        deferred.resolve();
                    }
                }

                request(options, callback);

                return deferred.promise;
            }

【讨论】:

  • 这段代码的问题是你不会在错误时调用deferred.reject(),并且你不会调用deferred.resolve(body),因为它实际上应该这样做。正如我所说,在手动 Promisification 中很容易出错,而这些错误使得正确使用 Promise 的潜力变得困难。
  • 查看我添加的其他答案。它显示了您的答案中的代码应该的样子。
猜你喜欢
  • 2016-08-13
  • 2018-02-28
  • 1970-01-01
  • 2014-08-30
  • 2016-03-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多