在nodejs中遇到promises问题

时间:2012-11-23 16:05:44

标签: javascript node.js promise

我正在尝试使用nodejs的promises(我正在尝试使用node-promise包);然而,没有任何成功。请参阅以下代码:

var express = require('express'),
    request = require('request'),
    promise = require('node-promise');

app.get('/promise', function(req, res) {
    var length = -1;

    new promise.Promise(request(
        {uri: "http://www.bing.com"},
        function (error, response, body) {
            if (error && response.statusCode !== 200) {
                console.log("An error occurred when connected to the web site");
                return;
            }

            console.log("I'll return: " + body.length);
            length = body.length;
        }
    )).then(function(result) {
        console.log("This is what I got: " + length);
        console.log("Done!");
    });

    res.end();
});

上述代码的输出仅为I'll return: 35857,不会转到then部分。

我将代码更改为:

app.get('/promise', function(req, res) {
    var length = -1;

    promise.when(
        request(
            {uri: "http://www.bing.com"},
            function (error, response, body) {
                if (error && response.statusCode !== 200) {
                    console.log("An error occurred when connected to the web site");
                    return;
                }

                console.log("I'll return: " + body.length);
                length = body.length;
            }
        ),
        function(result) {
            console.log("This is what I got: " + length);
            console.log("Done!");
        },
        function(error) {
            console.log(error);
        }
    );

    res.end();
});

这次输出是This is what I got: -1然后是Done! ...看起来这次没有调用“承诺”。

所以:

  • 修复上面的代码需要做些什么?显然我做得不对:)
  • 当我做承诺时节点承诺是“走的路”,还是有更好的方法/包?即更简单,更准备生产。

感谢。

2 个答案:

答案 0 :(得分:4)

试试jquery-deferred-for-node

我不是专家,但我知道这个lib往往受到服务器端和客户端工作的程序员的青睐。

即使你还不知道jQuery的Deferreds,走这条路的好处是:

  • 文档非常好(它包含指向jQuery文档的链接),但您可能很难找到特定于Node的示例。

  • 方法是可链接的。

  • jQuery Callbacks也包括在内。

  • 当有一天你需要在客户端进行异步操作时,几乎没有什么可以重新学习 - 概念是相同的,语法几乎是如此。请参阅上面超链接的github页面中的“Correspondances”部分。

修改

我不是node.js的人,所以我在这里猜测,但根据你上面的代码,你可能想要考虑jquery-deferred-for-node的以下几行:

var express = require('express'),
    request = require('request'),
    Deferred = require('JQDeferred');

function fetch(uri, goodCodes) {
    goodCodes = (!goodCodes) ? [200] : goodCodes;

    var dfrd = Deferred(); // A Deferred to be resolved/rejected in response to the `request()`.
    request(uri, function(error, response, body) {
        if (!error) {
            var isGood = false;

            // Loop to test response.statusCode against `goodCodes`.
            for (var i = 0; i < goodCodes.length; i++) {
                if (response.statusCode == goodCodes[i]) {
                    isGood = true;
                    break;
                }
            }

            if (isGood) {
                dfrd.resolve(response.statusCode, body);
            } else {
                dfrd.reject(response.statusCode, "An invalid response was received from " + uri);
            }
        } else {
            dfrd.reject(response.statusCode, "An error occurred attempting to connect to " + uri);
        }
    });

    // Make promise derived from dfrd available to "consumer".
    return dfrd.promise();
};

//...

app.get('/promise', function(req, resp) {
    fetch("http://www.bing.com").done(function(statusCode, result) {
        console.log("Done! This is what I got: " + result.length);
    }).fail(function(statusCode, message) {
        console.log("Error (" + statusCode + "): " + message);
    });
    resp.end();
};

在这里,我尝试编写一个通用实用程序来获取资源,以便可以在外部处理异步响应(或错误)。我认为这大致与你想要达到的目标一致。

出于兴趣,console.log()消息在哪里以node.js结束?

编辑2

上面,我给了Deferred一个初始资本,这对于构造函数是常规的

使用jQuery Deferreds,必须有多种方法连续获取()。下面的方法保留fetch()原样,并引入fetch_()作为其前端。可能有更简单的方法,但这允许fetch()保持一般功能,在功能上等同于客户端jQuery.ajax()

function fetch_(uri){
    return function(){
        return fetch(uri, [200]).then(function(statusCode, result){
            console.log("Done! This is what I got: " + result.length);
        },function(statusCode, message){
            console.log("Error (" + statusCode + "): " + message);
        });
    };
}

注意函数fetch()返回一个函数。它必须是这样的,因为在调用fetch()时,我们想要一个未执行的函数,而不是(还)该函数的结果。

现在让我们假设一个uris数组可用。无论应用程序需要什么,都可以进行硬编码或动态构建。

var uris = [
    'http://xxx.example.com',
    'http://yyy.example.com',
    'http://zzz.example.com'
];

现在,可以通过多种方式调用fetch_()

//v1. To call `resp.end()` when the fetching process starts.
app.get('/promise', function(req, resp) {
    fetch_(uris[0])().then(fetch_(uris[1])).then(fetch_(uris[2]));
    resp.end();
});

//v2. To call `resp.end()` when the fetching process has finished.
app.get('/promise', function(req, resp){
    fetch_(uris[0])().then(fetch_(uris[1])).then(fetch_(uris[2])).always(resp.end);
});

//v3. As v2 but building a `.then()` chain of any (unknown) length.
app.get('/promise', function(req, resp){
    var dfrd = Deferred().resolve();//
    $.each(uris, function(i, uri){
        dfrd = dfrd.then(fetch_(uri));
    });
    dfrd = dfrd.always(resp.end);
});

未测试

我对v1和v2更有信心。 v3可能有效。

v2和v3都应该给出完全相同的行为,但v3会针对任意数量的uris进行推广。

一切都可能需要调试。

答案 1 :(得分:3)

我建议使用Q:https://github.com/kriskowal/q。我相信它在其他框架内部使用(比如jQuery延迟实现)。

我认为文件“很好”;语法与其他promise实现一致......并且它有一个节点适配器。

所以你的延迟风格方法:

var deferred = Q.defer();
FS.readFile("foo.txt", "utf-8", function (err, res) {
    if (!err) {
        deferred.resolve(res);
    } else {
        deferred.reject(err);
    }
});
return deferred.promise;

可以更简洁地写成:

var deferred = Q.defer();
FS.readFile("foo.txt", "utf-8", deferred.makeNodeResolver());
return deferred.promise;