Knex 交易承诺警告

Knex transaction Promises warnings

我在使用 Knex、事务和 Promises 时遇到了困难,希望得到 SO 专家的帮助 :)

通过阅读类似的帖子和很多关于 Promises 的文章,我明白我离解决方案不远了,但发现下面的代码对于这样一个简单的任务来说真的过于复杂了。

这个 class 只是公开了一个函数,该函数接收先前构建的查询并且需要在首次调用 "load_user_settings" 函数后 运行 它(使用 PostgreSQL 9.5 作为数据库) .我实际上需要第二个查询的结果(或错误),所以简单地将 trx.commit 放在 "then" 中对我不起作用。

我的函数末尾的 process.nextTick 真的感觉像是一个巨大的 hack,添加它是为了避免将我的整个应用程序更改为 Promises,因为目前这是不可行的。如果没有它,我经常会发现自己收到 Promise 警告 ("Unhandled rejection Error") 并在我的代码稍后抛出异常时挂起代码。

此外,我有时会在日志中看到以下警告: 警告:承诺因非错误而被拒绝:[对象未定义] 我猜那个警告与这个问题有某种关系...

如果可以找到仅使用回调的解决方案,那也很好,因为我们的整个应用程序当前都在使用回调。

const pg = require('pg');
const async = require('async');

class MyClass {

    constructor(connectionInfo) {
        this.connectionInfo = connectionInfo; // This is an object containing user, password, database, host and port.

        this.pgKnex = require('knex')({
            client : 'pg',
            connection : this.connectionInfo,
            pool : {
                min : 1,
                max : 25
            }
        });
    }

    query(username, sql, params, callback) {
        let response;
        let error;

        this.pgKnex.transaction((trx) => {
            return this.pgKnex.raw(`SELECT load_user_settings(?)`, [username]).transacting(trx)
                .then(() => {
                    return this.pgKnex.raw(sql, params).transacting(trx);
                })
                .then((result) => {
                    response = result;
                    return trx.commit();
                }).catch((err) => {
                    error = err;
                    console.error(err);
                    return trx.rollback();
                });
        }).catch((err) => {
            error = err;
            console.error(err);
        }).then(() => {
            process.nextTick(() => {
                callback(error, response);
            });
        });
    }
}

module.exports = MyClass;

根据 Bergi 的评论,我编写了这个版本,它避免了任何黑客攻击并删除了我收到的所有承诺警告:

query(username, sql, params, callback) {
    this.pgKnex.transaction((trx) => {
        this.pgKnex.raw(`SELECT set_config('ims.username', ?, false)`, [username]).transacting(trx).asCallback((err, result) => {
            if(err) {
                trx.rollback().asCallback(() => {
                    console.error(err);
                    callback(err);
                });
                return;
            }

            this.pgKnex.raw(sql, params).transacting(trx).asCallback((err, result) => {
                if(err) {
                    trx.rollback().asCallback(() => {
                        console.error(err);
                        callback(err);
                    });
                    return;
                }

                trx.commit().asCallback((commitErr) => {
                    callback(commitErr, result);
                });
            });
        });
    }).asCallback((err, result) => {
        // Nothing to do here ... I guess?
    });
}

但我仍然觉得我可以改进...

让我试着简化一下:

query(username, sql, params, callback) {
    this.pgKnex.transaction((trx) => {
        return this.pgKnex.raw(`SELECT set_config('ims.username', ?, false)`, [username])
            .transacting(trx)
            .then(() => {
                return this.pgKnex.raw(sql, params)
                    .transacting(trx);
            });
    }).asCallback(callback);
}

当使用交易块时,您可以 return 一个 Promise 如果 Promise 是 resolved/rejected,knex 将自动 commit/rollback。

最后的 asCallback(callback) 调用确保承诺链中抛出的错误将传递给 callback。您不需要单独处理每个承诺的错误,因为承诺链的所有错误都会冒泡。同样,如果结果是 return 来自第二个 sql 查询(承诺链的最后一个),则此结果作为第二个参数传递给 callback.