我有类似以下的代码:
const _addEvents = async(eventList, day, transaction) => {
await eventList.forEach(event => {
return model.events.findOne({where:event, transaction: transaction}).then(event => {
if(!event) {
transaction.rollback();
throw new Error("event not found");
}
return event;
})
.then(event => {
day.addEvents(event, {through: {status: 'ENABLED'}});
day.save();
})
})
}
}
export const create = async(attributes) => {
return await sequelize.transaction(async(t) => {
return model.days.create(attributes, {transaction: t})).then(async(day) => {
await _addEvents(attributes.eventList, day, t);
return day;
})
})
}
我看到的是交易开始,并且在交易日内增加了一行。在第一次迭代中,在方法findOne
中调用_addEvents
时,将提交事务。
我使用过Sequelize.useCLS("foo");
,所以我希望所有交易都在同一个命名空间中。 为了确定,我正在传递交易。
任何人都可以告诉我为什么在执行findOne
之后提交事务,并告诉我如何避免这种情况的发生。
我正在使用(babel-core 6.26.3)和node.js v9.2.0&postgres 10.5
答案 0 :(得分:0)
_addEvents
正在执行异步工作,但是sequelize.transaction
不知道需要等待它完成。 CLS负责将事务传递到嵌套上下文中,但是您仍然需要确保正在等待所有查询完成。
await _addEvents(attributes.eventList, day, t);
在model.days.create
中,您需要将事务作为第二个参数传递,请参见http://docs.sequelizejs.com/class/lib/model.js~Model.html#static-method-create
通常,我建议使用 CLS或将事务传递到使用它的任何地方。
答案 1 :(得分:0)
感谢@Jan Aagaard Meier&Co揭示问题。
我的问题是await eventList.forEach
没有按照我的期望 做。
Array.prototype.forEach
将返回undefined
。它将返回与循环内的承诺状态无关的
因此之所以发生COMMIT,是因为在丢失的Promise(在循环内)可以完成之前,事务功能已经完成。
一种解决方案是使用bluebird's Promise.map将该行变成
await Promise.map(eventList, async(event) => {
await model.events.findOne({where:event, transaction: transaction}).then(event => {
....
}
});
这里地图将等到所有子Promise都完成,并且当我们返回Promise(并等待它)时,我们确保我们不会在Map完成之前完成交易。