内部函数或调用函数是否应该包含在 try-catch 块中以进行正确的错误处理?

Should the inner function or the calling function be wrapped within a try-catch block for correct error handling?

是否应该将内部函数或调用函数包装在 try-catch 块中以进行正确的错误处理?

示例 1

// Async function definition
const getLatestRecords = async() => {
    try {
        const records = await Record.find({})
        return records
    } catch(e) {
        console.error(e)
    }
}

// Async function call without error handling - will this suffice?
let records

const callAsyncFunction = async() => {
    records = await getLatestRecords()
}

callAsyncFunction()

// Same async function call with error handling - or should it be like this?
let records

const callAsyncFunctionWithTryCatch = async() => {
    try {
        records = await getLatestRecords()
    } catch(e) {
        console.error(e)
    }   
}

callAsyncFunctionWithTryCatch()

Q1) 在上面的示例中,异步函数定义 getLatestRecords() 已经包含在 try-catch 块中以防出错处理。尽管如此,由 callAsyncFunction() 定义的调用函数是否足够,或者异步函数调用是否也应该包装在 try-catch 块中,如 callAsyncFunctionWithTryCatch 所示()?

示例 2

// Test without try catch block - will this suffice?
test('Should delete records with valid id', async() => {
    const record = await new Record().save()

    const res = await request(app)
        .delete(`/records/${record._id}`)
        .expect(200)
    
    expect(await Records.countDocuments()).toBe(0)
})

// Test with try catch block - should all tests with await in them be wrapped in a try catch block?
test('Should delete records with valid id', async() => {
    try {
        const record = await new Record().save()

        const res = await request(app)
            .delete(`/records/${record._id}`)
            .expect(200)
        
        expect(await Records.countDocuments()).toBe(0)
    } catch(e) {
        console.error(e)
    }
})

Q2) 同样,对于测试,是否应该将所有带有 await 调用的测试包装在 try catch 块中?请记住,有人希望测试在出现问题时失败,所以我对此不太确定。

如果您知道可以在该函数中处​​理与错误有关的所有事情,则只应将它们包含在try/catch中。否则,您应该让函数的 consumer 决定如何处理错误。例如,对于 getLatestRecords,如果在使用它的脚本周围有各种不同的点,那么让 这些不同的点 处理错误可能是最有意义的,具体取决于他们的背景。例如:

const getLatestRecords = async () => {
  const records = await Record.find({})
  return records
}
// could also just do: `return Record.find({})`
getLatestRecords()
  .then(doStuff)
  .catch((err) => {
    res.status(500).send('Could not retrieve records for ' + user);
  });

如果将 try/catch 放在内部 getLatestRecords 函数中,另一方面,消费者将不得不通过检查 return 值存在,而不是 .catch,这有点奇怪。

如果所有 await 都在 try/catch 内,包含 async 的函数将永远不会拒绝。因此,让 async 函数的调用者将它放在 try/catch 中不会完成任何事情,因为内部函数永远不会拒绝; catch 永远不会被输入。也就是说,对于这个例子:

const getLatestRecords = async() => {
    try {
        const records = await Record.find({})
        return records
    } catch(e) {
        console.error(e)
    }
}
const callAsyncFunctionWithTryCatch = async() => {
    try {
        records = await getLatestRecords()
    } catch(e) {
        console.error(e)
    }   
}

callAsyncFunctionWithTryCatch()

多此一举;简化为

const getLatestRecords = async() => {
    try {
        const records = await Record.find({})
        return records
    } catch(e) {
        console.error(e)
    }
}
const callAsyncFunctionWithTryCatch = async() => {
    records = await getLatestRecords()
}

callAsyncFunctionWithTryCatch()

(或仅在 callAsyncFunctionWithTryCatch 中捕获 - 但不要在 两者中捕获

  1. should the async function call also be wrapped in a try-catch block

如果一个 async 函数被包装在 try/catch 中,你不需要在多个层次上使用它,除非有一些代码可能会抛出错误并且没有被 try/catch 包装=].例如:

const getLatestRecords = async() => {
    aFunctionWhichCanThrow(); // <= Only need try/catch in caller fn for this
    try {
        const records = await Record.find({})
        return records
    } catch(e) {
        console.error(e)
    }
}

即使在那种情况下,您也可能希望通过全局 unhandledRejection 处理程序来处理错误。

  1. should all tests with await calls in them be wrapped in a try catch block

是的,可以 throw/reject 的测试应该包含在 try/catch 块中。这些案例可能是这样的:

  • 函数应该抛出
    • try - 验证函数响应
    • 赶上 - 测试失败
  • 函数应该抛出
    • 尝试 - 测试失败
    • catch - 验证错误