我被告知Promise.all解决其承诺的顺序无法保证。但是,我没有看到它从Promise.all native docs按顺序解析。
因此,以下knex迁移方法不应该起作用,因为messages
具有对users表的引用。
然而,在几次迁移过程中,我从未碰到过一次实例,因为竞争条件存在错误。意思是,似乎Promise.all根据索引位置解析。
所以,我的问题是:以下片段是否容易出现竞争状况?
return Promise.all([
knex.schema.createTable('users', function(table) {
table.increments().primary();
...
}),
knex.schema.createTable('messages', function(table) {
table.increments().primary();
table.bigInteger('user_id').unsigned().index()
.references('id').inTable('users');
}),
这是更好的方法吗?
return Promise.all([
knex.schema.createTable('users', function(table) {
table.increments().primary();
...
}),
]).then(function() {
return Promise.all([
knex.schema.createTable('messages', function(table) {
table.increments().primary();
table.bigInteger('user_id').unsigned().index()
.references('id').inTable('users');
}),
});
})
答案 0 :(得分:1)
由于您有一个依赖项,因为您需要创建第一个表,然后才能在第二个表中引用它,您应该使用then
- way。
Promise.all
是否会看到其承诺是否按索引顺序解决,与Promise.all
本身无关,而是将各个承诺作为参数传递给它。虽然您可以期望JavaScript(而不是Promise.all
)按顺序评估参数列表,但您通常无法知道哪些承诺将首先解析。这取决于个人承诺,而不是Promise.all
。
在你的情况下,这些单独的promise会做类似的事情,即创建一个表,你的数据库引擎可能会按顺序处理这些语句,而不是并发,你可能在实践中看到promises总是按你列出的顺序解析他们,但依靠这一点是不好的做法。
请注意,如果您将仅包含一个承诺的数组传递给Promise.all
,则可以跳过Promise.all
并立即将then
应用于该单一承诺。
答案 1 :(得分:0)
我希望此代码位于迁移文件中,默认情况下会创建隐式事务。因此,由于查询被发送到同一个连接,因此它们不会被并行解析。
在这种情况下,数据库驱动程序(至少pg
)实际缓冲第二个查询并等待直到第一个查询被解析,然后再将第二个查询发送到数据库服务器。
唯一重要的是这两个查询以哪种顺序执行并发送到pg-driver。
由于Promise.all
并不保证它会在knex.schema.createTable('users',...)
之前执行knex.schema.createTable('messages',...)
查询,这意味着理论上可能会出现第一个代码失败的情况。
tl; dr 第一种方法不容易出现争用情况,但如果查询执行顺序错误,则可能会失败。第二种方法更好。