验证参数并在函数中返回错误很常见。
但是,在JavaScript回调函数中,例如:
function myFunction(num, callback) {
if (typeof num !== 'number') return callback(new Error('invalid num'))
// do something else asynchronously and callback(null, result)
}
我写了很多像这样的函数,但我想知道是否存在可能有害的东西。因为在大多数情况下,调用者假定这是一个异步函数,并且回调将在函数调用之后的代码之后执行。但是如果某些参数无效,该函数将立即调用回调。因此调用者必须小心处理这种情况,即一个意外的执行序列。
我想听听有关这个问题的一些建议。我应该小心地假设所有异步回调都可以立即执行吗?或者我应该使用类似setTimeout(...,0)的东西将同步事物转换为异步事物。或者有一个我不知道的更好的解决方案。感谢。
答案 0 :(得分:5)
API应记录它将同步调用回调 (如Array#sort
)或异步调用(如Promise#then
),然后始终遵守记录的内容保证。它不应该混合搭配。
所以,是的,如果你有一个通常会异步调用回调的函数,它应该总是异步调用它,无论它为何进行调用。
jQuery中有一个很好的例子:当jQuery首次添加“延迟”对象时,如果延迟已经解决,它们会同步调用回调,但如果没有,则异步调用异步。这是许多混淆和错误的根源,这也是ES2015承诺保证then
和catch
回调始终被异步调用的原因之一。
如果可能且与代码库的其余部分不相符,请查看使用Promises而不是简单的回调。 Promise为异步操作(以及与同步操作的互操作)提供非常清晰,简单,有保证的语义和可组合性。
答案 1 :(得分:1)
不,立即回电是没有害处的,事实上故意拖延错误只会浪费时间和开销。是的,立即回拨错误可能是非常有害的,应该避免一个假定为异步的函数! (看那个,180!)
从开发人员的角度来看,为什么设置只能在之后才能完成,有很多充分的理由。例如here:
const server = net.createServer(() => {}).listen(8080);
server.on('listening', () => {});
在调用listening
之后才会附加.listen(8080)
事件,因为事件源是从.listen()
的调用返回的。在这种情况下,实现listening
事件以在执行.listen()
后同步调用将不会成功。
这是我想提出的另一个案例:
var num = '5';
myFunction(num, function callback(err, result) {
if (err) {
return myFunction(num, callback);
}
// handle result
});
现在,如果callback
同步出错,则此控制流将导致堆栈溢出。虽然这是开发人员的错,但是从预期为异步的函数发生堆栈溢出是一件非常糟糕的事情。这是使用setImmediate()
传递错误而不是立即执行callback
的一个优点。
答案 2 :(得分:1)
异步函数的调用者应该知道调用函数的结果是什么。异步函数应返回的标准是Promises。
如果您的函数返回Promise
,任何人都可以轻松了解该函数中发生了什么。 Promises有拒绝回调,但我们可以争论是否应该通过拒绝承诺来处理参数的验证,或者是否应该直接抛出异常。无论哪种方式,如果调用者使用catch
方法正确处理异常,则将以相同的方式捕获直接抛出的异常和拒绝。
function throwingFunction(num) {
return new Promise(function (resolve, reject) {
if (typeof num !== 'number') throw new Error('invalid num');
// do something else asynchronously and callback(null, result)
};
}
function rejectingFunction(num) {
return new Promise(function (resolve, reject) {
if (typeof num !== 'number') reject(new Error('invalid num'));
// do something else asynchronously and callback(null, result)
};
}
// Instead of passing the callback, create the promise and provide your callback to the `then` method.
var resultThrowing = throwingFunction(num)
.then(function (result) { console.log(result); })
.catch(function (error) { console.log(error); });
var resultRejecting = rejectingFunction(num)
.then(function (result) { console.log(result); })
.catch(function (error) { console.log(error); });
这两种模式都会导致捕获并记录错误。
如果您使用promises,异步函数的调用者将不必担心您在函数内部的实现,并且您可以直接抛出错误或拒绝承诺。