简体   繁体   English

Q Promise链和NodeJS回调

[英]Q Promise chain and NodeJS callbacks

I think this is a really stupid question but I'm having a hard time wrapping my head around promises. 我认为这是一个非常愚蠢的问题,但我很难兑现承诺。

I'm using Q (for nodejs) to sync up a couple of async functions. 我正在使用Q(用于nodejs)来同步几个异步函数。 This works like a charm. 这就像一个魅力。

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

The problem I have is with this little function: 我有这个小功能的问题:

  getItemData(itemRequest) 

This function is filled with several of callbacks. 此函数包含多个回调。 The promise chain runs through the function just fine but ignores all the callbacks I use ( eg several XHR calls I make in the function). promise链在整个函数中运行得很好,但是忽略了我使用的所有回调(例如,我在函数中进行的几个XHR调用)。

A simplified version of the function looks like this (just to give you an idea): 该函数的简化版本如下所示(仅供参考):

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

The xhr function I use looks like this: 我使用的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); 
        }
    });
  }

So my questions: 所以我的问题是:

  • Can I leave the function unaltered and use the callbacks that are in place ánd the promise chain? 我是否可以保留功能不变,并使用承诺链和适当的回调?
  • Or do I have to rewrite the function to use promises for the XHR? 还是我必须重写函数以对XHR使用Promise?
  • And if so, How can I best write my promise chain? 如果是这样,我该如何最好地写出我的诺言链? Should I reject the initial promise in the forEach? 我应该拒绝forEach中的最初承诺吗?

Again, sorry if this is a really stupid question but I don't know what the right course of action is here. 再次,如果这是一个非常愚蠢的问题,对不起,但我不知道这里的正确做法是什么。

Thanks! 谢谢!

[EDIT] Q.nfcall, I don't get it [编辑] Q.nfcall,我不明白

So I've been looking into Q.nfcall which allows me to use node callbacks. 因此,我一直在研究Q.nfcall,它使我可以使用节点回调。 Bu I just don't understand exacly how this works. Bu我只是不完全了解它是如何工作的。 Could someone give a simple example how I would go about using it for a function with several async xhr calls? 有人可以举一个简单的例子,说明如何将它用于具有多个异步xhr调用的函数吗?

I tried this but as you can see I don't really understand what I'm doing: 我尝试过此方法,但是如您所见,我不太了解自己在做什么:

    var second = Q.nfcall(second);

    function second (files) {

[EDIT 2] [编辑2]

This is the final funcction in my getitemdata function callback chain. 这是我的getitemdata函数回调链中的最后一个功能。 This function basically does the same as the function 'second' but I push the result directly and then return the promise. 该函数与“ second”函数基本相同,但我直接推送结果,然后返回promise。 This works as stated, but without all the additional callback data, because it does not wait for the callbacks to return with any data. 如上面所述,它可以工作,但是没有所有其他回调数据,因为它不等待回调返回任何数据。

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

Your answer is not really clear after your second edit. 第二次编辑后,您的答案还不清楚。

First, on your orignal question, your getItemData has no influence on the promise chain. 首先,在您的原始问题上,您的getItemData对承诺链没有影响。
You could change you the function's call signature and pass your deferred promise like so. 您可以更改函数的调用签名,并像这样传递您的延期承诺。

getItemData(itemRequest, d) 

and pass this deferred promises all the way to your xhrCall and resolve there. 并将这个递延的承诺一直传递到您的xhrCall并在那里解决。

I would re-write your whole implementation and make sure all your functions return promises instead. 我将重新编写您的整个实现,并确保所有函数都返回promise。

Many consider deferred promises as an anti-pattern. 许多人将延迟的承诺视为反模式。 So I use use the Promise API defined in harmony (the next javascript) 所以我使用和谐定义的Promise API(下一个javascript)

After said that, I would re-implement your original code like so (I've not tested) 说完之后,我将像这样重新实现您的原始代码(我尚未测试)

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)

Note that there is no done method on the Promise API. 请注意, Promise API上没有done方法。

One down side of the new Promise API is error handling. 新的Promise API的缺点之一是错误处理。 Take a look at bluebird . 看看bluebird
It is a robust promise library which is compatible with the new promise API and has many of the Q helper functions. 它是一个健壮的Promise库,它与新的Promise API兼容并且具有许多Q帮助器功能。

As far as I can tell, you need to return a promise from getItemData . 据我所知,您需要从getItemData返回一个getItemData Use Q.defer() as you do in second() , and resolve it when the callbacks complete with the data. 像在second() Q.defer()一样使用Q.defer() ,并在回调完成数据后解析它。 You can then push that into list . 然后,您可以将其推入list

To save code, you can use Q.nfcall to immediately call a node-style-callback function, and return a promise instead. 若要保存代码,可以使用Q.nfcall立即调用节点样式回调函数,并返回一个Promise。 See the example in the API docs: https://github.com/kriskowal/q/wiki/API-Reference#qnfcallfunc-args 请参阅API文档中的示例: https : //github.com/kriskowal/q/wiki/API-Reference#qnfcallfunc-args

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM