我正在处理AWS JS SDK提供的承诺。当我创建包装AWS SDK的异步函数时,我所做的事情的主旨如下:
module.exports.myCustomFunction = input => {
if (badInput) {
throw new Error('failed') // <-- This is they key line
}
return sns.createTopic(someParams).promise()
}
// The caller looks like this:
myModule.myCustomFunction(someInput).then(result => {
// carry on
})
.catch(err => {
// do something with the error
})
有人说我不应该在这些基于承诺的函数中抛出错误。他们建议改回Promise.reject('failed')
。说实话,我还没有那么精通承诺,所以他们的解释有点过了我的脑袋。
答案 0 :(得分:3)
承诺/承诺链中的throw
会自动导致承诺/承诺链被拒绝。
const myFunc = input => {
return new Promise((resolve, reject) => {
if (badInput) {
throw new Error('failed')
}
return resolve(sns.createTopic(input).promise())
})
}
return myFunc('some input')
.then(result => {
// handle result
})
.catch(err => { console.log(err) }) // will log 'failed'
但是,您的myCustomFunction
未包含在Promise中,它在throw
返回Promise之前使用sns.createTopic().promise()
。要创建并返回已处于拒绝状态的承诺,您可以使用Promise.reject(new Error('failed'))
代替throw
。
答案 1 :(得分:2)
他们是对的。
对myCustomFunction
的调用假设始终返回一个承诺(.then
和.catch
分别处理已解决和拒绝的承诺)。当您抛出错误时,该函数不会返回一个承诺。
你可以使用它来捕获错误:
try {
myModule.myCustomFunction(someInput).then(result => {
// carry on
})
.catch(err => {
// do something with the error
})
} catch(err) {
...
}
但正如您所看到的,这会产生两个错误处理程序:try/catch
表示同步抛出的错误,.catch
表示sns.createTopic(someParams)
可能返回的任何拒绝承诺。
这就是为什么使用Promise.reject()
更好:
module.exports.myCustomFunction = input => {
if (badInput) {
return Promise.reject('failed');
}
return sns.createTopic(someParams).promise()
}
然后,.catch
将捕获两种类型的错误/拒绝。
注意:对于较新版本的Node.js(相信v7.6及更高版本),以下内容也适用:
module.exports.myCustomFunction = async input => {
if (badInput) {
throw new Error('failed');
}
return sns.createTopic(someParams).promise()
}
此处的关键是async
关键字。通过使用此关键字,函数结果将自动包含一个承诺(类似于@peteb&#39的答案所示)。
答案 2 :(得分:0)
永远不会在这些基于承诺的函数中引发错误
在我看来,你应该同步抛出TypeError()
,因为它表示程序员错误而不是操作错误
操作错误 表示正确编写的程序遇到的运行时问题。这些不是程序中的错误。事实上,这些通常是其他问题[...]
程序员错误 是程序中的错误。通过更改代码可以始终避免这些错误。它们永远不能被正确处理(因为根据定义,有问题的代码被破坏了。)
您的同事无法区分这些类型的错误,并且您编写的代码几乎应该是,除了使用通用Error()
而不是语义上纠正TypeError()
。
您最初是说您正在为AWS SDK编写包装器。因此,从使用您的库的开发人员的角度来看,您是否认为他们更喜欢调试一个程序,该程序会立即抛出他们做错的事情,或者他们更喜欢调试无声地失败的程序,试图解决他们滥用您的API而不通知他们错误的代码?
如果您认为第一个选项听起来更容易处理,那么您绝对是正确的。在告诉程序员他们做错了什么时,你的程序应该始终尽可能透明。尝试解决滥用问题导致错误的API出现未定义,未记录且仅仅是奇怪的奇怪行为。
为了给出一个非常基本的例子(可能是不公平的比较,因为我没有关于badInput
的内容的任何背景),你的同事告诉你你应该这样做:
try {
if (badInput) {
throw new Error('failed')
}
...
} catch (error) {
// expected error, proceed as normal
// ...except not really, you have a bug
}
而不是:
process.on('uncaughtException', function (error) {
// deal with programmer errors here and exit gracefully
})
if (badInput) {
throw new Error('failed')
}
try {
...
} catch (error) {
// deal with operational errors here and continue as normal
}
在the chart here中可以找到Node.js运行时环境中用于区分这些错误的一些真实示例,即使在异步函数中也是如此:
Example func | Kind of func | Example error | Kind of error | How to | Caller uses
| | | | deliver |
==========================================================================================
fs.stat | asynchronous | file not found | operational | callback | handle callback
| | | | | error
-------------+--------------+----------------+---------------+----------+-----------------
fs.stat | asynchronous | null for | programmer | throw | none (crash)
| | filename | | |
我会留给您判断您的特定问题是由于程序员错误还是操作错误,但总的来说,给您的建议不是合理的建议,并鼓励错误的程序,试图继续进行,好像没有任何错误。
在错误导致错误的情况下,预期在操作条件下返回Promise
的函数应该throw
同步,并且当错误发生在错误内容时应该reject
异步写程序。
这反映了Joyent的官方建议:
从程序员错误中恢复的最佳方法是立即崩溃。