在(嵌套)try/catch/finally 块中处理 nodejs 中的错误

Handling errors in nodejs in (nested) try/catch/finally blocks

我正在重构我的 nodejs 代码。我正在使用 node-postgres 实现事务。我的代码现在看起来像这样:

const controller = async (req, res, next) => {
    const client = await pool.connect()

    try {

        // get the user ID out of the params in the URL
        const { user_id } = req.params

        let query, values, result

        try {
            await client.query('BEGIN')
        } catch (err) {
            throw new CustomError(err)
        }

        try {
            query = 'QUERY_1'
            values = ['VALUES_1']
            result  = await client.query(query, values)
        } catch (err) {
            throw new CustomError(err)
        }

        // handle some stuff

        try {
            query = 'QUERY_2'
            values = ['VALUES_2']
            result  = await client.query(query, values)
        } catch (err) {
            throw new CustomError(err)
        }

        // handle some more stuff

        try {
            await client.query('COMMIT')
        } catch (err) {
            throw new CustomError(err)
        }

        return res.status(200).json({ response: 'ok' })
    } catch (err) {
        await client.query('ROLLBACK')

        return next(new CustomHandleError(400, 'something_went_wrong'))
    } finally {
        client.release()
    }
}

此代码有效,但我有一些问题。我是 nodejs 的初学者。

1) 在我的 'finally' 块中,我将客户端释放回池中。但是当一切正常时,我 return 在 'try' 块中响应。在网上我读到 'finally' 块总是被执行,所以在 try 块中 return 一个(好的)响应并在 finally 块中释放客户端是否可以?

2)嵌套多个try catch块是否可以(或者是否反模式)。原因是 node-postgres 会抛出错误,但我想 return 将所有错误都发送给自定义错误处理程序,因此我首先捕获这些错误,然后在我的 CustomError 处理程序中再次抛出它们。

提前致谢!

1) 是的,这是很好的做法。

2) 我不认为它自动成为一种反模式,但如果我能找到一种更简洁的方法,我会避免使用它。

Robert C. Martin 在其书中的建议的经验法则 'Clean Code':

if the keyword 'try' exists in a function, it should be the very first word in the function and that there should be nothing after the catch/finally blocks.

如果有机会,请避免嵌套 try-catch。 分成不同的功能

您可以显着简化您的 try/catch 处理,因为所有内部 catch 块都做同样的事情并且不是必需的:

const controller = async (req, res, next) => {
    const client = await pool.connect()

    try {

        // get the user ID out of the params in the URL
        const { user_id } = req.params

        let query, values, result;
        await client.query('BEGIN');
        query = 'QUERY_1'
        values = ['VALUES_1']
        result  = await client.query(query, values)

        // handle some stuff

        query = 'QUERY_2'
        values = ['VALUES_2']
        result  = await client.query(query, values)

        // handle some more stuff

        await client.query('COMMIT')
        return res.status(200).json({ response: 'ok' })
    } catch (err) {
        await client.query('ROLLBACK')
        return next(new CustomHandleError(400, 'something_went_wrong'))
    } finally {
        client.release()
    }
}

那么,对于你的问题:

1) In my 'finally' block, I release the client back to the pool. But when everything is OK, I return the response in the 'try' block. Online I read that the 'finally' block is ALWAYS executed, so is it OK to return a (good) response in the try block and releasing the client in the finally block?

是的,这很好用 finally

2) Is it OK (or is it anti-pattern) to nest multiple try catch blocks. The reason is that the node-postgres throws errors but I want to return all errors to a custom error handler, so I catch those errors first and then throw them again in my CustomError handler.

没关系(实现特定目标时不是反模式),但在这种情况下没有必要,因为你所有的内部 catch() 块都做同样的事情并且都被捕获你的外部捕获块,这样你就可以保留外部捕获并摆脱所有内部捕获。如果你的所有 await 语句在我的代码中拒绝,你所有的 await 语句将直接进入你的外部 catch

需要内部捕获的一些原因是:

  1. 您想创建一个自定义错误(每个异步操作都不同),您将在函数的最终结果中实际使用它。

  2. 您想 "handle" 本地错误并继续处理不同的代码路径,而不仅仅是直接错误 return。例如,您尝试加载一个配置文件,捕获错误并在配置文件不存在的情况下使用默认值继续执行函数中的其余代码。