访问较早的promise下游中的变量

时间:2014-03-18 16:41:36

标签: javascript node.js promise q

基本上在下面的脚本中,我想知道导致失败状态的请求的URL。

我是否需要将其封装在对象中并继续向下游传递?

var Q = require('q')
var _ = require('underscore')
var JSON = require('JSON')
var FS = require("q-io/fs");
var HTTP = require("q-io/http");


FS.read('members.json').then(function(memberJson){
    return JSON.parse(memberJson)
}).then(function(memberObjects){

    var httpCheckPromises = _.chain(memberObjects)
        .first(50)
        .filter(function(member){
            return member.website.toLowerCase().indexOf('www') >= 0
        })
        .map(function(member){
            return HTTP.read(member.website)
        })
        .value()

    return Q.allSettled(httpCheckPromises)

}).then(function(responses){

    return  _.chain(responses)
        .where({state:'rejected'})
        .pluck('reason')
        .pluck('response')
        .value() ;

}).then(function(badResponses){

    return _.chain(badResponses)
        .filter(function(response) {
            return response
        })
        .map(function(response){
            return {
                status: response.status
                , headers: response.headers
            }
        })
        .value()

}).then(function(responses){
    return FS.write("members_with_bad_urls.json", JSON.stringify(responses,null,4))
}).then(function(){
    console.log('DONE!')
}).fail(function(reason){
    console.log('FAIL!')
    console.log(reason)
})

例如在我的第二个代码块中添加一个返回对象

 then(function(memberObjects){

    var httpCheckPromises = _.chain(memberObjects)
        .first(50)
        .filter(function(member){
            return member.website.toLowerCase().indexOf('www') >= 0
        })
        .map(function(member){
            return {
                url: member.website
                ,response: HTTP.read(member.website)
            }
        })
        .value()

    return Q.allSettled(httpCheckPromises)

})

但我认为我的问题需要重写Q.allSettled。

2 个答案:

答案 0 :(得分:2)

Akaphenom,为了在链条上提供结果,你总是可以使用一个或多个外部变量,但使用这种类型的模式可能更干净:

promise_returning_function().then(function(x) {
    return { x: x } ;//this object will be passed all the way down the .then chain.
}).then(function(obj) {
    //here you can read obj.x
    obj.y = ...;//add result of this process to obj
    //return obj;
}).then(function(obj) {
    //here you can read obj.x and obj.y
    obj.z = ...;//add result of this process to obj
    //return obj;
}).done(function(obj) {
    //here you can read obj.x and obj.y and obj.z
});

为了说明模式,我在每个阶段向obj添加了一个属性(根据问题中的代码),但您可以添加任意数量的属性 - 无,一个或多个 - 无论后续阶段需要什么。

不幸的是,通过以这种方式传播obj.then()返回新承诺的力量就会丧失。但是,以下模式的变体克服了这一点:

promise_returning_function().then(function(x) {
    var promise_x = ...;//do something asynchronous here
    return Q.when(promise_x, function(result_x) {
        return { x: result_x };//this object will be passed all the way down the .then chain.
    });
}).then(function(obj) {
    //here you can read obj.x
    var promise_y = ...;//do something asynchronous here
    return Q.when(promise_y, function(result_y) {
        obj.y = result_y;
        return obj;
    });
}).then(function(obj) {
    //here you can read obj.x and obj.y
    var promise_z = ...;//do something asynchronous here
    return Q.when(promise_z, function(result_z) {
        obj.z = result_z;
        return obj;
    });
}).done(function(obj) {
    //here you can read obj.x and obj.y and obj.z
});

也许这种模式可以称为"。然后瀑布"?

代码相当冗长,外部var obj = {};肯定会更简洁,但希望这种模式具有一定的潜在用途。

修改

这是一个 DEMO ,我已经提取了重复的代码并创建了辅助函数propagate()

EDIT2

more convincing version of the DEMO ,每个阶段都有setTimeout延迟。

编辑3

这是辅助函数propagate()

的模式
function propagate(p, obj, prop) {
    return Q.when(p, function(result) {
        obj[prop] = result;
        return obj;
    });
}
promise_returning_function().then(function(x) {
    var promise_x = ...; //do something asynchronous here
    return propagate(promise_x, {}, 'x');
}).then(function(obj) {
    //here you can read obj.x
    var promise_y = ...; //do something asynchronous here
    return propagate(promise_y, obj, 'y');
}).then(function(obj) {
    //here you can read obj.x and obj.y
    var promise_z = ...; //do something asynchronous here
    return propagate(promise_z, obj, 'z');
}).done(function(obj) {
    //here you can read obj.x and obj.y and obj.z
});

答案 1 :(得分:0)

我将原始HTTP请求从与请求一起传递的response.node.req对象中拉出来。不幸的是 - 不是我的orignial问题的答案,而是我的问题的解决方案。不幸的是我 - 我引用了一个由下划线证明的变量,这让我有些紧张。对于我运行一次的简单脚本,没关系 - dbut不是一个很好的解决方案。

then(function(badResponses){

    return _.chain(badResponses)
        .filter(function(response) {
            return response
        })
        .map(function(response){
            var req = response.node.req

            return {
                requestUri: req._headers.host + req.path
                , httpStatusDesc: friendlyHttpStatus(response.status)
                , httpStatus: response.status
                , movedTo: response.headers.location
            }
        })
        .value()

})