2013-09-05 59 views
0

我认为这是一个非常愚蠢的问题,但我很难将我的头包裹在promise中。Q Promise链和NodeJS回调

我正在使用Q(for 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功能最终funcction回调链。这个函数基本上和函数'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); 
}; 
+0

你似乎没有将任何回调传递给'getItemData'?此外,如果你的问题是关于这个功能,你应该认真的在你的问题中包括它的代码... – Bergi

+1

只需使用'var first = Q.nfbind(fs.readdir,path);':-) – Bergi

+0

请你详细说明一下吗?另外,我已经按要求添加了整个功能。感谢您的时间! – jansmolders86

回答

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
这是一个强大的承诺库,它与新的承诺API兼容,并有许多Q辅助函数。

0

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

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

+0

谢谢你的答案。但我想我有点在做你的建议。我更新了我的问题,以便在我解析函数的回调链中看到最终的函数。我做错了吗?再次感谢! – jansmolders86