在函数中验证参数并返回错误是很常见的。
但是,在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) 的方法将同步事物转换为异步事物。或者有更好的解决方案我不知道。谢谢。
API 应记录它将调用回调同步(如Array#sort
)或异步(如
Promise#then
),然后始终遵守记录的保证。它不应该混合搭配。 所以,是的,如果您有一个通常会异步调用回调的函数,那么它应该
始终异步调用它,无论它为何进行调用。
jQuery 中有一个很好的例子:当 jQuery 第一次添加“延迟”对象时,如果延迟已经解决,它们会同步调用回调,但如果还没有解决,则异步调用。这是许多混乱和错误的根源,这也是为什么 ES2015 的承诺保证 then
和
catch
回调将始终被异步调用的部分原因。
Promises 而不是简单的回调。 Promise 为异步操作(以及与同步操作的互操作)提供了非常清晰、简单、有保证的语义和可组合性。
如果您的函数返回
Promise
,任何人都可以轻松理解该函数中发生了什么。 Promise 有拒绝回调,但我们可能会争论是否应该通过拒绝 Promise 来处理参数的验证,或者是否应该直接抛出异常。无论哪种方式,如果调用者使用
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); });
两种模式都会导致错误被捕获并记录。
如果您使用 Promise,异步函数的调用者将不必担心函数内部的实现,并且您可以直接抛出错误或根据需要拒绝 Promise。
不,立即回调并没有什么害处,事实上,故意延迟错误只会浪费时间和开销。是的,立即回调错误可能非常有害,对于假设的函数应该避免异步! (看那个,180!)
这里:
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()
的优点之一。我建议使用 TypeScript 进行类型检查。 在您的情况下,请尝试以下操作:
callback