2014-08-29 85 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