rus*_*sx2 12 javascript asynchronous node.js promise bluebird
我试图了解一些不那么琐碎的承诺/异步用例.在我目前正在努力的一个例子中,我有一个从knex查询(可成组数组)返回的书籍数组我希望插入到数据库中:
books.map(function(book) {
// Insert into DB
});
Run Code Online (Sandbox Code Playgroud)
每个书籍项目如下:
var book = {
title: 'Book title',
author: 'Author name'
};
Run Code Online (Sandbox Code Playgroud)
但是,在我插入每本书之前,我需要从一个单独的表中检索作者的ID,因为这些数据是标准化的.作者可能存在也可能不存在,因此我需要:
但是,上述操作也都是异步的.
我可以在原始映射中使用promise(获取和/或插入ID)作为插入操作的先决条件.但问题在于,因为所有内容都是异步运行的,所以代码可能会插入重复的作者,因为初始check-if-author-exists与insert-a-new-author块分离.
我可以想到实现上述目标的几种方法,但它们都涉及拆分承诺链,而且通常看起来有点混乱.这似乎是一种必须出现的问题.我确定我在这里缺少一些基本的东西!
有小费吗?
我们假设您可以并行处理每本书.然后一切都很简单(仅使用ES6 API):
Promise
.all(books.map(book => {
return getAuthor(book.author)
.catch(createAuthor.bind(null, book.author));
.then(author => Object.assign(book, { author: author.id }))
.then(saveBook);
}))
.then(() => console.log('All done'))
Run Code Online (Sandbox Code Playgroud)
问题是获取作者和创建新作者之间存在竞争条件.请考虑以下事件顺序:
现在我们在作者表中有两个A实例.这是不好的!要解决这个问题,我们可以使用传统方法:锁定.我们需要保留每个作者锁的表.当我们发送创建请求时,我们锁定适当的锁.请求完成后,我们解锁它.涉及同一作者的所有其他操作需要在执行任何操作之前先获取锁.
这似乎很难,但在我们的案例中可以简化很多,因为我们可以使用我们的请求promises而不是lock:
const authorPromises = {};
function getAuthor(authorName) {
if (authorPromises[authorName]) {
return authorPromises[authorName];
}
const promise = getAuthorFromDatabase(authorName)
.catch(createAuthor.bind(null, authorName))
.then(author => {
delete authorPromises[authorName];
return author;
});
authorPromises[author] = promise;
return promise;
}
Promise
.all(books.map(book => {
return getAuthor(book.author)
.then(author => Object.assign(book, { author: author.id }))
.then(saveBook);
}))
.then(() => console.log('All done'))
Run Code Online (Sandbox Code Playgroud)
而已!现在,如果对作者的请求是飞行,则将返回相同的承诺.