2014-08-29 59 views
0

我想向用戶發送新書的列表。到目前爲止,下面的代碼工作正常。問題是我不想多次發送一本書,所以我想過濾它們。多個Q.all內部函數?

當前的代碼工作正常:

function checkActiveBooks(books) { 
    var queue = _(books).map(function(book) { 
    var deferred = Q.defer(); 

    // Get all alerts on given keywords 
    request('http://localhost:5000/books?l=0&q=' + book.name, function(error, response, body) { 
     if (error) { 
     deferred.reject(error); 
     } 

     var books = JSON.parse(body); 
     if (!_.isEmpty(books)) { 

     // Loop through users of current book. 
     var userBooks = _(book.users).map(function(user) { 

      // Save object for this user with name and deals. 
      return { 
       user: user, 
       book: book.name, 
       books: books 
      } 

     }); 

     if (_.isEmpty(userBooks)) { 
      deferred.resolve(null); 
     } else { 
      deferred.resolve(userBooks); 
     } 
     } else { 
     deferred.resolve(null); 
     } 

    }); 

    return deferred.promise; 

    }); 

    return Q.all(queue); 
} 

但現在我想已經過濾發書:

function checkActiveBooks(books) { 
    var queue = _(books).map(function(book) { 
     var deferred = Q.defer(); 
     // Get all alerts on given keywords 
     request('http://localhost:5000/books?l=0&q=' + book.name, function(error, response, body) { 
      if (error) { 
       deferred.reject(error); 
      } 
      var books = JSON.parse(body); 
      if (!_.isEmpty(books)) { 
       // Loop through users of current book. 
       var userBooks = _(book.users).map(function(user) { 
        var defer = Q.defer(); 
        var userBook = user.userBook.dataValues; 
        // Check per given UserBook which books are already sent to the user by mail 
        checkSentBooks(userBook).then(function(sentBooks) { 
         // Filter books which are already sent. 
         var leftBooks = _.reject(books, function(obj) { 
          return sentBooks.indexOf(obj.id) > -1; 
         }); 
         // Save object for this user with name and deals. 
         var result = { 
          user: user, 
          book: book.name, 
          books: leftBooks 
         } 
         return deferred.resolve(result); 
        }); 
        return Q.all(userBooks); 
       } else { 
        deferred.resolve(null); 
       } 
      }); 
      return deferred.promise; 
     }); 
     return Q.all(queue); 
    } 

但上面的代碼不起作用。它不停止循環。我認爲使用q.all兩次是有意義的,因爲它包含兩個循環。但我想我做錯了...

+0

你說的「它不會停止循環」是什麼意思? – Bergi 2014-08-29 11:33:14

+0

我看到的唯一問題是'checkSentBooks'可能在所有'checkActiveBooks'' request()'完成之前被調用。您的服務器上是否存在競爭狀況,這些競爭情況取決於對方? – Bergi 2014-08-29 11:34:43

+0

那麼它確實停止循環,但我沒有返回一個響應,我的不好。但checkSentBook是異步的這一事實是一個問題。它也包含一個承諾,因爲checkSentBook使用Sequelize JS來調用數據庫。這就是我有兩個q.all的原因。我現在的問題是,.map函數不會等待checkSentBook函數返回數據庫結果,因此數組中的書對象保持爲空。雙重q.all不起作用。檢查本傑明的答案,並回答下面的答案。 – 2014-08-29 11:36:45

回答

0

代理上@本傑明的建議,這裏是代碼會是什麼樣當checkSentBooks返回一個承諾,如:

var request = Q.nfbind(require("request")); // a promised version. 
function checkActiveBooks(books) { 
    return Q.all(_(books).map(function(book) { 
     // a callback with multiple arguments will resolve the promise with 
     // an array, so we use `spread` here 
     return request('http://localhost:5000/books?l=0&q=' + book.name).spread(function(response, body) { 
      var books = JSON.parse(body); 
      if (_.isEmpty(books)) return null; 
      return Q.all(_(book.users).map(function(user) { 
       return checkSentBooks(user.userBook.dataValues).then(function(sentBooks) { 
//    ^^^^^^ return a promise to the array for `Q.all` 
        return { 
         user: user, 
         book: book.name, 
         books: _.reject(books, function(obj) { 
          return sentBooks.indexOf(obj.id) > -1; 
         }) 
        }; 
       }); 
      })); 
     }); 
    })); 
} 
+0

謝謝,我略有不同,但它幾乎相同。它工作:)謝謝你的幫助,Bergi和Benjamin! – 2014-08-29 12:12:43

+0

檢查dat嵌套:P – 2014-08-29 15:00:39

2

首先,你應該總是在最低級別promisify。你在這裏複雜化並且有多個延期。通常,只有在將API轉換爲承諾時纔會有延遲。承諾鏈和撰寫所以讓我們做到這一點:)

var request = Q.nfbind(require("request")); // a promised version. 

這可以使你的代碼在頂部變成:

function checkActiveBooks(books) { 
    return Q.all(books.map(function(book){ 
     return request('http://.../books?l=0&q=' + book.name) 
       .get(1) // body 
       .then(JSON.parse) // parse body as json 
       .then(function(book){ 
        if(_.isEmpty(book.users)) return null; 
        return book.users.map(function(user){ 
         return {user: user, book: book.name, books: books }; 
        }); 
       }); 
    }); 
} 

這在我看來,很多更優雅。

現在,如果我們想通過謂詞來過濾他們,我們可以這樣做:

function checkActiveBooksThatWereNotSent(books) { 
     return checkActiveBooks(books).then(function(books){ 
      return books.filter(function(book){ 
        return checkSentBooks(book.book); 
       }); 
     }); 
} 

值得一提的是,藍鳥庫中有實用方法這一切就像Promise#filterPromise#map那將會使代碼更短。

注意,如果checkSentBook是異步你需要稍微修改代碼:

function checkActiveBooksThatWereNotSent(books) { 
     return checkActiveBooks(books).then(function(books){ 
      return Q.all(books.map(function(book){ // note the Q.all 
        return Q.all([book, checkSentBooks(book.book)]); 
       })).then(function(results){ 
        return results.filter(function(x){ return x[1]; }) 
            .map(function(x){ return x[0]; }); 
       }); 
     }); 
} 

就像我說的,有不同的庫這看起來要好很多。以下是代碼在Bluebird中的樣子,它的速度也快兩個數量級,並且具有良好的堆棧跟蹤和檢測未處理的拒絕。爲了娛樂和榮耀,我扔了ES6箭頭和速記屬性:

var request = Promise.promisify(require("request")); 

var checkActiveBooks = (books) => 
    Promise. 
    map(books, book => request("...&q=" + book.name).get(1)). 
    map(JSON.parse). 
    map(book => book.users.length ? 
     book.users.map(user => {user, books, book: book.name) : null)) 

var checkActiveBooksThatWereNotSent = (books) => 
    checkActiveBooks(books).filter(checkBookSent) 

我發現好多了。

+0

謝謝你的回答!看起來非常整齊,的確如此。但是'checkSentBook''是異步的這個事實是我的問題。它也包含一個承諾,因爲'''checkSentBook''會使用Sequelize JS調用數據庫。這就是我有兩個q.all的原因。你有這個解決方案嗎?我必須在'''book.users.map''循環中調用''''checkSentBook'''函數。我現在的問題是'''.map'''函數不會等待'''checkSentBook''函數返回數據庫結果,所以我的書籍對象在數組中保持爲空。 – 2014-08-29 10:02:04

+0

如果我沒有在'''book.users.map'''函數內調用''checkSentBook'',我必須更多的代碼,因此我必須將兩個大數組與大物體的數量。試過昨天,所以在'''book.users.map''循環內調用''checkSentBook''似乎是最好和最簡單的解決方案。只有它是異步的這一事實,對我來說很複雜。 – 2014-08-29 10:08:59

+1

@ErikVandeVen很好!這實際上不是很難處理!我正在編輯我的答案,假設'checkSentBooks'正確地返回一個承諾(而不是回調)。 – 2014-08-29 12:20:41