Q Promise链和NodeJS回调

时间:2013-09-05 15:40:19

标签: javascript node.js promise deferred q

我认为这是一个非常愚蠢的问题,但我很难绕过承诺。

我正在使用Q(用于nodejs)来同步几个异步函数。 这就像一个魅力。

    var first = function () {
        var d = Q.defer();
        fs.readdir(path,function(err,files){
            if(err) console.log(err);
            d.resolve(files);
        });
        return d.promise;
    };

    var second = function (files) {
        var list = new Array;
        files.forEach(function(value, index){
            var d = Q.defer();
            console.log('looking for item in db', value);
            db.query(
                'SELECT * FROM test WHERE local_name =? ', [value],{
                    local_name      : String,

                },
                function(rows) {
                    if (typeof rows !== 'undefined' && rows.length > 0){
                        console.log('found item!', rows[0].local_name);
                        d.resolve(rows[0]);
                    } else {
                        var itemRequest = value;
                        getItemData(itemRequest);
                    }
                }
            );
            list.push(d.promise);
        });
        return Q.all(list);
    };

    first()
    .then(second)
    .done(function(list){
        res.send(list);
    });

我遇到的问题是这个小功能:

  getItemData(itemRequest) 

这个函数充满了几个回调。 promise链运行正常,但忽略了我使用的所有回调(例如我在函数中进行的几次XHR调用)。

该函数的简化版本看起来像这样(只是为了给你一个想法):

    function getItemData(itemRequest){
        helper.xhrCall("call", function(response) {
            var requestResponse = JSON.parse(response)
            , requestInitialDetails = requestResponse.results[0];

            downloadCache(requestInitialDetails,function(image) {

                    image = localImageDir+requestInitialDetails.image;

                    helper.xhrCall("call2", function(response) {

                        writeData(item,image,type, function(){
                            loadData(item);
                        });
                    });
                } else {
                    writeData(item,image,type, function(){
                        loadData(item);
                    });
                }
            });
        });

我使用的xhr函数如下所示:

  xhrCall: function (url,callback) {
    var request = require("request")
    , colors = require('colors');
    request({
        url: url,
        headers: {"Accept": "application/json"},
        method: "GET"
    }, function (error, response, body) {
        if(!error){
            callback(body);
        }else{
           console.log('Helper: XHR Error',error .red); 
        }
    });
  }

所以我的问题:

  • 我可以保持功能不变并使用现有的回调和承诺链吗?
  • 或者我是否必须重写函数以使用XHR的promises?
  • 如果是这样,我怎样才能最好地写下我的承诺链?我应该拒绝forEach中的最初承诺吗?

再次,对不起,如果这是一个非常愚蠢的问题,但我不知道这里有正确的行动方案。

谢谢!

[编辑] Q.nfcall,我不明白

所以我一直在研究Q.nfcall,它允许我使用节点回调。我只是不明白这是如何工作的。 有人可以给出一个简单的例子,我将如何将它用于具有多个异步xhr调用的函数?

我试过了,但你可以看到我真的不明白我在做什么:

    var second = Q.nfcall(second);

    function second (files) {

[编辑2]

这是我的getitemdata函数回调链中的最终函数。此函数基本上与函数'second'相同,但我直接推送结果然后返回promise。这可以按照说明工作,但没有所有额外的回调数据,因为它不会等待回调与任何数据一起返回。

  function loadData(item) {
        var d = Q.defer();
        db.query(
            'SELECT * FROM test WHERE local_name =? ', [item],{
                local_name      : String,

            },
            function(rows) {
                if (typeof rows !== 'undefined' && rows.length > 0){
                    list.push(d.promise);
                } 
            }
        );

    });
    return Q.all(list);
};

2 个答案:

答案 0 :(得分:1)

第二次修改后你的回答并不是很清楚。

首先,在您的原始问题上,您的getItemData对承诺链没有任何影响 您可以更改功能的呼叫签名并传递您的延期承诺。

getItemData(itemRequest, d) 

并将此延期承诺一直传递给您的xhrCall并在那里解决。

我会重新编写整个实现,并确保所有函数都返回promises。

许多人认为延期承诺是一种反模式。所以我使用了和谐定义的Promise API(下一个javascript)

在说完之后,我会重新实现你原来的代码(我还没有测试过)

var Promise = Promise || require('es6-promise').Promise // a polyfill
;

function errHandler (err){
  throw err
}

function makeQuery () {
  var queryStr = 'SELECT * FROM test WHERE local_name =? '
  , queryOpt = {local_name: String}
  ;
  console.log('looking for item in db', value)

  return new Promise(function(resolve, reject){
    db.query(queryStr, [value], queryOpt, function(rows) {
      if (typeof rows !== 'undefined' && rows.length > 0){
        console.log('found item!', rows[0].local_name);
        resolve(rows[0]);
      } else {
        // note that it returns a promise now.
        getItemData(value).then(resolve).catch(errHandler)
      }
    })
  })
}

function first () {
  return new Promise(function(resolve, reject){
    fs.readdir(path, function(err, files){
      if (err) return reject(err)
      resolve(files)
    })
  })
}

function second (files) {
  return Promise.all(files.map(function(value){
    return makeQuery(value)
  });
}

first()
.then(second)
.then(res.send)
.catch(errHandler)

请注意,done API上没有Promise方法。

Promise API的一个缺点是错误处理。看看bluebird 它是一个健壮的promise库,它与新的promise API兼容,并具有许多Q辅助函数。

答案 1 :(得分:0)

据我所知,您需要从getItemData返回承诺。像在Q.defer()中一样使用second(),并在回调完成数据时解决它。然后,您可以将其推送到list

要保存代码,可以使用Q.nfcall立即调用节点样式回调函数,并返回一个promise。请参阅API文档中的示例:https://github.com/kriskowal/q/wiki/API-Reference#qnfcallfunc-args