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;
})
};
});
}));
});
}));
}