为什么我们应该使用 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():

当我们使用Promise.all():

所以我会说 Promise.all 更好,因为:

我错过了什么?
这是否可能会导致其他问题:例如断开的连接被 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);

您甚至不需要为此进行交易,除非您的某些独立查询更改了数据。