【问题标题】:Q Promise chain and NodeJS callbacksQ Promise 链和 NodeJS 回调
【发布时间】:2013-09-05 15:40:19
【问题描述】:

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

我正在使用 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) 

这个函数充满了几个回调。承诺链很好地贯穿函数,但忽略了我使用的所有回调(例如,我在函数中进行的几个 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 的 Promise?
  • 如果是这样,我怎样才能最好地编写我的承诺链?我应该拒绝 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);
};

【问题讨论】:

  • 您似乎没有向getItemData 传递任何回调?此外,如果您的问题是关于该功能的,您应该认真地将其代码包含在您的问题中......
  • 只需使用var first = Q.nfbind(fs.readdir, path); :-)
  • 您能详细说明一下吗?此外,我已按要求添加了整个功能。感谢您的宝贵时间!

标签: javascript node.js promise deferred q


【解决方案1】:

第二次编辑后,您的答案不是很清楚。

首先,关于您的原始问题,您的getItemData 对承诺链没有影响。
您可以更改函数的调用签名并像这样传递您的延迟承诺。

getItemData(itemRequest, d) 

并将这个延迟的承诺一直传递给您的xhrCall 并在那里解决。

我会重写你的整个实现,并确保你的所有函数都返回 Promise。

许多人认为延迟承诺是一种反模式。所以我使用了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)

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

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

【讨论】:

    【解决方案2】:

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

    为了节省代码,您可以使用 Q.nfcall 立即调用 node-style-callback 函数,并返回一个 promise。请参阅 API 文档中的示例:https://github.com/kriskowal/q/wiki/API-Reference#qnfcallfunc-args

    【讨论】:

    • 谢谢斯图尔特的回答。但我想我正在做你建议的事情。我已经更新了我的问题,因此您可以在我的回调链中看到我解析函数的最终函数。我做错了吗?再次感谢!
    猜你喜欢
    • 2015-01-19
    • 1970-01-01
    • 1970-01-01
    • 2017-01-09
    • 1970-01-01
    • 1970-01-01
    • 2013-02-05
    • 1970-01-01
    • 2015-01-24
    相关资源
    最近更新 更多