【问题标题】:Get access to a variable in an earlier promise downstream访问下游早期承诺中的变量
【发布时间】:2014-03-22 06:50:00
【问题描述】:

基本上在下面的脚本中,我想知道导致失败状态的请求的 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 的问题。

【问题讨论】:

    标签: javascript node.js promise q


    【解决方案1】:

    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
    });
    

    也许这种模式可以称为“.then 瀑布”?

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

    编辑

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

    编辑2

    还有一个 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
    });
    

    【讨论】:

    • 哦,人性化的方法,比我考虑的替代方法好得多。我有一个解决我当前问题的解决方案(尽管这种方法更好),在我的解决方案中提到,它绕过了这个问题。我打算再开放一两天,看看我们是否会征求更多解决方案。一个模式,可能值一千字。
    • 这真是一个超级答案。代码很干净,它解决了我期望会变得突出的问题
    【解决方案2】:

    我将原始 HTTP 请求从随请求一起传播的 response.node.req 对象中提取出来。不幸的是 - 不是我原始问题的答案,而是我的问题的解决方案。不幸的是 II - 我引用了一个由下划线证明的变量,这让我有点紧张。对于我运行一次的简单脚本来说还可以 - 但不是一个很好的解决方案。

    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()
    
    })
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-09-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-03-27
      • 1970-01-01
      相关资源
      最近更新 更多