为什么我们应该使用 batch() 而不是 Promise.all?
Why should we use batch() instead of Promise.all?
来自 pg-promise FAQ Why use method batch instead of promise.all?:
It is quintessential to settle all the promises-queries created within your task or transaction, before the connection is released
我不明白为什么这应该是个问题。
对于示例,当我们有这样一组查询时:
[
t.any("SELECT pg_sleep(2) as a"),
t.any('this will fail'),
t.any("SELECT pg_sleep(3) as b")
]
注意:pg_sleep
仅用于测试。
在生产中,这将是 Insert/Update/Delete
语句。我们只想在所有事务都成功时提交事务:即 return 任何一个失败时都会出错。
当我们使用batch():
- 第一个承诺将在 2 秒后解决
- 第二个承诺将被拒绝
- 第三个查询仍将发送到数据库,并在 3 秒后 returns
- 最后(总共 5 秒后),
batch
完成,我们可以 return 向调用者报告错误。
当我们使用Promise.all():
- 第一个承诺将在 2 秒后解决
- 第二个承诺将被拒绝——这将回滚事务并释放数据库连接
- 现在我们已经可以 return 给调用者一个错误
- 第三个请求将立即失败并显示
Querying against a released or lost connection.
。无论如何,这是意料之中的,所以我们可以忽略它。
所以我会说 Promise.all
更好,因为:
- 在第一个错误
之后立即 return 秒
- 甚至不会向数据库发送第 3 个无用的查询
我错过了什么?
这是否可能会导致其他问题:例如断开的连接被 return 发送到池等
方法batch适用于可能创建动态数量查询的情况。
它确保所有查询都得到解决(解决或拒绝),因此您不会以针对已关闭的连接执行查询而告终,并得到 Querying against a released or lost connection
错误。它可以是 bad/confusing,开始让这些错误发生在上下文之外,并且您无法诊断发生了什么。
方法Promise.all
不结算承诺,当数组中的第一个承诺拒绝时,它停止处理并拒绝。
虽然方法 batch 仍然非常有用,因为它在如何处理值方面更灵活,并且比 Promise.all
提供更好的 result/error 细节,但它在今天的使用不再需要。它是在 ES5 时代开发的,当时 async/await
还不存在。但今天您可以轻松地将其替换为 async/await
:
旧样式:
db.task('get-all-records', t => {
return t.batch([
t.any('SELECT * FROM apples'),
t.any('SELECT * FROM oranges')
]);
})
.then([apples, oranges] => {
// process data here
})
.catch(error => {});
新款式:
const {apples, oranges} = await db.task('get-all-records', async t => {
const apples = await t.any('SELECT * FROM apples');
const oranges = await t.any('SELECT * FROM oranges');
return {apples, oranges};
});
上面两个例子的结果是一样的,虽然在执行逻辑上不一样,第一个是完全异步的,而后者使用async/await
,这是阻塞的操作,如果之前的查询失败,它们甚至会阻止您创建下一个查询。
额外内容
在执行多个独立查询(彼此不依赖)时,性能最佳的方法是连接所有查询,并将它们全部作为一个查询执行。
为此,有方法 helpers.concat, plus database method multi 来处理多个结果:
const queries = [
{query: 'SELECT * FROM apples WHERE color = ', values: ['green']},
'SELECT * FROM oranges'
];
const sql = pgp.helpers.concat(queries);
const [apples, oranges] = await db.multi(sql);
您甚至不需要为此进行交易,除非您的某些独立查询更改了数据。
来自 pg-promise FAQ Why use method batch instead of promise.all?:
It is quintessential to settle all the promises-queries created within your task or transaction, before the connection is released
我不明白为什么这应该是个问题。
对于示例,当我们有这样一组查询时:
[
t.any("SELECT pg_sleep(2) as a"),
t.any('this will fail'),
t.any("SELECT pg_sleep(3) as b")
]
注意:pg_sleep
仅用于测试。
在生产中,这将是 Insert/Update/Delete
语句。我们只想在所有事务都成功时提交事务:即 return 任何一个失败时都会出错。
当我们使用batch():
- 第一个承诺将在 2 秒后解决
- 第二个承诺将被拒绝
- 第三个查询仍将发送到数据库,并在 3 秒后 returns
- 最后(总共 5 秒后),
batch
完成,我们可以 return 向调用者报告错误。
当我们使用Promise.all():
- 第一个承诺将在 2 秒后解决
- 第二个承诺将被拒绝——这将回滚事务并释放数据库连接
- 现在我们已经可以 return 给调用者一个错误
- 第三个请求将立即失败并显示
Querying against a released or lost connection.
。无论如何,这是意料之中的,所以我们可以忽略它。
所以我会说 Promise.all
更好,因为:
- 在第一个错误 之后立即 return 秒
- 甚至不会向数据库发送第 3 个无用的查询
我错过了什么?
这是否可能会导致其他问题:例如断开的连接被 return 发送到池等
方法batch适用于可能创建动态数量查询的情况。
它确保所有查询都得到解决(解决或拒绝),因此您不会以针对已关闭的连接执行查询而告终,并得到 Querying against a released or lost connection
错误。它可以是 bad/confusing,开始让这些错误发生在上下文之外,并且您无法诊断发生了什么。
方法Promise.all
不结算承诺,当数组中的第一个承诺拒绝时,它停止处理并拒绝。
虽然方法 batch 仍然非常有用,因为它在如何处理值方面更灵活,并且比 Promise.all
提供更好的 result/error 细节,但它在今天的使用不再需要。它是在 ES5 时代开发的,当时 async/await
还不存在。但今天您可以轻松地将其替换为 async/await
:
旧样式:
db.task('get-all-records', t => {
return t.batch([
t.any('SELECT * FROM apples'),
t.any('SELECT * FROM oranges')
]);
})
.then([apples, oranges] => {
// process data here
})
.catch(error => {});
新款式:
const {apples, oranges} = await db.task('get-all-records', async t => {
const apples = await t.any('SELECT * FROM apples');
const oranges = await t.any('SELECT * FROM oranges');
return {apples, oranges};
});
上面两个例子的结果是一样的,虽然在执行逻辑上不一样,第一个是完全异步的,而后者使用async/await
,这是阻塞的操作,如果之前的查询失败,它们甚至会阻止您创建下一个查询。
额外内容
在执行多个独立查询(彼此不依赖)时,性能最佳的方法是连接所有查询,并将它们全部作为一个查询执行。
为此,有方法 helpers.concat, plus database method multi 来处理多个结果:
const queries = [
{query: 'SELECT * FROM apples WHERE color = ', values: ['green']},
'SELECT * FROM oranges'
];
const sql = pgp.helpers.concat(queries);
const [apples, oranges] = await db.multi(sql);
您甚至不需要为此进行交易,除非您的某些独立查询更改了数据。