Node + Sequelize:如何在添加项目之前检查其是否存在?(异步混乱)

33

很不幸,我对Node还很陌生,关于Node的异步/同步执行会有些困惑。

我正在使用Node、sequelize和sqlite以及async.js。

我有一系列的“文章”,每篇文章都有若干个“作者”。

对于每个“文章”的每个“作者”,我想检查是否存在该作者。如果不存在,则创建它。

问题是,在初始运行时,由于异步功能导致检查存在性出现了问题,会出现重复创建作者的情况。

例如,对于数组:authors = ['A. Test', 'B. Test', 'C. Test', 'A. Test']

以及以下代码:

async.each(authors, function(item, callback){
    Author.sync().then(function(){
      Author.count({ where: {name: item.trim()} }).then(function(count){
        if (count != 0) {
          console.log('Author already exists')
        } else {
          console.log('Creating author...')
          Author.create({
            name: item.trim()
          })
        }
      })
    })
  })

在第一次运行时,将创建一个表:

ID | name
------------
0  | A. Test
1  | B. Test
2  | C. Test
3  | A. Test

我做错了什么?我似乎缺少了在Node中异步与同步执行的基本概念。

(我也尝试过async.eachSeries,它应该是按顺序而非并行执行的?)

编辑:稍微重构了一下,但仍然会创建重复项

async.eachSeries(authors, function(authorName, callback){
    Author.findOne({ where: {name: authorName.trim()} }).
    then(function(author){
      if (author) {
        // Author exists...
        callback()
      } else {
        // Author does not exist...
        Author.create({
          name: authorName.trim()
        }).then(function(author){
          callback()
        })
      }
    })
  })
2个回答

35

Author.count这一行其实不是必需的,除非你需要计数。可以参考findOrCreate()

使用findOrCreate(),你可以像下面这样做。(根据trex005的代码片段进行编辑)

async.eachSeries(authors, function(item, callback) {
  Author.sync().then(function() {
    Author.findOrCreate({
      where: {
        name: item.trim()
      },
      defaults: { // set the default properties if it doesn't exist
        name: item.trim()
      }
    }).then(function(result) {
      var author = result[0], // the instance of the author
        created = result[1]; // boolean stating if it was created or not

      if (!created) { // false if author already exists and was not created.
        console.log('Author already exists');
      }

      console.log('Created author...');
      callback();
    });
  })
})


嗨,感谢回复。实际上我尝试了 findOrCreate(并重构了一些更干净的代码片段添加到我的答案中),但是 findOrCreate 在 sqlite 中引起了各种问题,比如数据库锁定等 - 显然这也会发生在某些 postgres 用户身上。不幸的是,我的新代码片段仍然无法正常工作,并且仍然会创建重复项。 - waffl
1
@joshua-f 顺便说一下,你可以使用扩展运算符代替 then 方法来分离 Promise 参数。例如:.spread(function(author, isCreated) { ... }); - Ali BARIN
@AliBARIN 哦,太好了,我不知道这一点。 - Joshua F
在异步函数中调用sync()有什么原因吗? - Ronen Teva
@RonenTeva 不,那个应该放在异步函数的外面。 - Joshua F

0

将每个 each 更改为 eachSeries,实际调用回调函数,你就可以得到正确的结果。

async.eachSeries(authors, function(item, callback){
    Author.sync().then(function(){
      Author.count({ where: {name: item.trim()} }).then(function(count){
        if (count != 0) {
          console.log('Author already exists')
          callback(); //assuming you want it to keep looping, if not use callback(new Error("Author already exists"))
        } else {
          console.log('Creating author...')
          Author.create({
            name: item.trim()
          }).then(function(author){
            callback();
          })
        }
      })
    })
  })

嗯,看起来这个不起作用,它只添加了第一个作者。 - waffl
你的Author.create方法是否支持像我在评论中提到的回调函数? - trex005
抱歉,您是什么意思?create方法是由Sequelize实现的(http://docs.sequelizejs.com/en/latest/api/model/#createvalues-options-promiseinstance),它会返回一个Promise。 - waffl
好的,它可以运行,谢谢!只是为了清楚我对流程的理解,调用的 callback() 函数是什么?我发现 async.js 文档 的措辞很令人困惑。 - waffl
它告诉循环继续执行。 - trex005
抱歉,经过进一步测试,该函数仍然会创建重复的结果 :( - waffl

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接