同步执行 Sequelize 查询

8
我正在使用Node.js和Sequelize(具有Postgres后端)构建网站。我有一个查询返回许多带外键的对象,我想将外键所引用的对象列表传递给视图。
在这个例子中,Attendances包含Hackathon键,我想返回一个Hackathons列表。由于代码是异步的,因此在Node中以下内容当然不起作用:
models.Attendance.findAll({
    where: {
        UserId: req.user.id
    }
}).then(function (data) {
    var hacks = [];
    for (var d in data) {
        models.Hackathon.findOne({
            where: {
                id: data[d].id
            }
        }).then(function (data1) {
            hacks.append(data1);
        });
    }
    res.render('dashboard/index.ejs', {title: 'My Hackathons', user: req.user, hacks: hacks});
});

有没有一种同步的方式来执行这个查询呢?也就是说,只有当“hacks”列表中填满了所有对象时,我才返回视图?

谢谢!


你尝试过使用async模块的waterfall吗?那可以帮助你。 - abdulbarik
在循环中查找一个记录是一种可怕的设计方式。应该只有一个查询。 - vitaly-t
3个回答

6
使用Promise.all来执行所有查询,然后调用下一个函数。

models.Attendance.findAll({
    where: {
        UserId: req.user.id
    }
}).then(function (data) {
    // get an array of the data keys, (not sure if you need to do this)
    // it is unclear whether data is an object of users or an array. I assume
    // it's an object as you used a `for in` loop
    const keys = Object.keys(data)
    // map the data keys to [Promise(query), Promise(query), {...}]
    const hacks = keys.map((d) => {
      return models.Hackathon.findOne({
        where: {
          id: data[d].id
        }
      })
    })
    // user Promise.all to resolve all of the promises asynchronously
    Promise.all(hacks)
      // this will be called once all promises have resolved so
      // you can modify your data. it will be an array of the returned values
      .then((users) => {
        const [user1, user2, {...}] = users
        res.render('dashboard/index.ejs', {
          title: 'My Hackathons', 
          user: req.user, 
          hacks: users
        });
      })
});


3
Sequelize库有一个“include”参数,可以在一次调用中合并模型。将你的where语句调整到将“Hackathons”模型引入“Attendance”中。如果这样做不起作用,请花费必要的时间正确设置Sequelize,他们的文档正在不断改进。最终,通过减少错误并使代码易读给其他程序员,你将节省大量时间。
看看下面的代码可以变得多么简洁...
models.Attendance.findAll({
    include: [{
        model: Hackathon,
        as: 'hackathon'
    },
    where: {
        UserId: req.user.id
    }
}).then(function (data) {
    // hackathon id
    console.log(data.hackathon.id)

    // attendance id
    console.log(data.id)
})

此外...
Hackathon.belongsTo(Attendance)
Attendance.hasMany(Hackathon)
sequelize.sync().then(() => {
  // this is where we continue ...
})

在这里了解更多有关 Sequelize includes 的信息: http://docs.sequelizejs.com/en/latest/docs/models-usage/


你真的可以过于谨慎。如果你只是因为“可能”而将所有内容都包装在parseInt()调用中,那么你就是在进行仪式崇拜。 - theraccoonbear
@theraccoonbear - 更新了代码,不需要使用parseInt()函数,当列被指定为type: INTEGER时,Sequelize会处理它。 - buycanna.io

0

立即调用异步函数表达式

这是在 如何在顶层使用async/await? 中提到的技术之一。截至2021年,顶层等待可能很快就会到来,这将更加完善。

最小可运行示例:

const assert = require('assert');
const { Sequelize, DataTypes } = require('sequelize');

const sequelize = new Sequelize({
  dialect: 'sqlite',
  storage: 'db.sqlite',
});
const IntegerNames = sequelize.define(
  'IntegerNames', {
  value: { type: DataTypes.INTEGER, allowNull: false },
  name: { type: DataTypes.STRING, },
}, {});

(async () => {
await IntegerNames.sync({force: true})
await IntegerNames.create({value: 2, name: 'two'});
await IntegerNames.create({value: 3, name: 'three'});
await IntegerNames.create({value: 5, name: 'five'});

// Fill array.
let integerNames = [];
integerNames.push(await IntegerNames.findOne({
  where: {value: 2}
}));
integerNames.push(await IntegerNames.findOne({
  where: {value: 3}
}));

// Use array.
assert(integerNames[0].name === 'two');
assert(integerNames[1].name === 'three');

await sequelize.close();
})();

在 Node v14.16.0、sequelize 6.6.2、seqlite3 5.0.2 和 Ubuntu 20.10 上进行了测试。


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