思考JavaScript承诺(在这种情况下蓝鸟)

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,因为这些数据是标准化的.作者可能存在也可能不存在,因此我需要:

  • 检查作者是否存在于DB中
  • 如果是,请使用此ID
  • 否则,插入作者并使用新ID

但是,上述操作也都是异步的.

我可以在原始映射中使用promise(获取和/或插入ID)作为插入操作的先决条件.但问题在于,因为所有内容都是异步运行的,所以代码可能会插入重复的作者,因为初始check-if-author-exists与insert-a-new-author块分离.

我可以想到实现上述目标的几种方法,但它们都涉及拆分承诺链,而且通常看起来有点混乱.这似乎是一种必须出现的问题.我确定我在这里缺少一些基本的东西!

有小费吗?

vku*_*kin 8

我们假设您可以并行处理每本书.然后一切都很简单(仅使用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)

问题是获取作者和创建新作者之间存在竞争条件.请考虑以下事件顺序:

  • 我们试图为作者B获得作者A;
  • 让作者A失败;
  • 我们要求创建作者A,但它尚未创建;
  • 我们试图为作者C获得作者A;
  • 让作者A失败;
  • 我们要求创建作者A(再次!);
  • 第一次请求完成;
  • 第二个请求完成;

现在我们在作者表中有两个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)

而已!现在,如果对作者的请求是飞行,则将返回相同的承诺.