Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/432.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 多重Q.all内部函数?_Javascript_Underscore.js_Promise_Q - Fatal编程技术网

Javascript 多重Q.all内部函数?

Javascript 多重Q.all内部函数?,javascript,underscore.js,promise,q,Javascript,Underscore.js,Promise,Q,我想向用户发送新书列表。到目前为止,下面的代码运行良好。问题是我不想多次发送一本书,所以我想过滤它们 现行守则运作良好: function checkActiveBooks(books) { var queue = _(books).map(function(book) { var deferred = Q.defer(); // Get all alerts on given keywords request('http://localhost:5000/book

我想向用户发送新书列表。到目前为止,下面的代码运行良好。问题是我不想多次发送一本书,所以我想过滤它们

现行守则运作良好:

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两次是有意义的,因为它包含两个循环。但我想我做错了…

首先,你应该始终在最低级别上承诺。你把这里的事情搞得很复杂,而且有多次延期。通常,只有在将API转换为承诺时才应该有延迟。承诺链和组成,让我们这样做:)

这会使顶部的代码变成:

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); 
                 });
      });
}
值得一提的是,Bluebird库为所有这一切提供了实用方法,如
Promise#filter
Promise#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中的样子,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)

我觉得更好。

根据@Benjamins的建议,下面是当
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;
                        })
                    };
                });
            }));
        });
    }));
}

谢谢你的回答!看起来确实很整洁。但是我的问题是,
checkSentBook
是异步的。它还包含一个承诺,因为
checkSentBook
使用Sequelize JS调用数据库。这就是我有两个问题的原因。你有解决这个问题的办法吗?我必须调用
book.users.map
循环中的
checkSentBook
函数。我现在的问题是,
.map
函数没有等待
checkSentBook
函数返回数据库结果,因此数组中的books对象保持为空。如果我不调用
book.users.map
函数中的
checkSentBook
,我必须更正更多的代码,因为我必须比较两个大数组和大量的对象。昨天试过了,所以在
book.users.map
循环中调用
checkSentBook
似乎是最好、最简单的解决方案。只是因为它是异步的,所以对我来说很复杂。@ErikVandeVen非常好!这其实并不难对付!我在编辑我的答案时假设
checkSentBooks
正确地返回了一个承诺(而不是接受回调)。为了好玩和荣耀…我明白了:-p你说的“它不会停止循环”是什么意思?我看到的唯一问题是
checkSentBooks
可能在所有
checkActiveBooks
请求()完成之前被调用。您的服务器上是否存在相互依赖的竞争条件?它确实停止了循环,但我没有返回响应,我的坏消息。但是checkSentBook是异步的这一事实是一个问题。它还包含一个承诺,因为checkSentBook使用Sequelize JS调用数据库。这就是我有两个问题的原因。我现在的问题是.map函数不等待checkSentBook函数返回数据库结果,因此数组中的MyBooks对象保持为空。双q都不行。检查下面本杰明的答案和我对这个答案的回答。我已经获得了重新缩进代码的自由。如你所见,某处缺少一个括号。双
Q.all
应该可以工作;尝试Benjamin的建议,即承诺使用函数并重写代码,而不调用
Q.defer
。谢谢,我的建议略有不同,但几乎相同。它是有效的:)谢谢你的帮助,贝基和本杰明!
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;
                        })
                    };
                });
            }));
        });
    }));
}