2013-09-05 94 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