JavaScript 回调错误处理

问题描述 投票:0回答:4

在函数中验证参数并返回错误是很常见的。

但是,在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) 的方法将同步事物转换为异步事物。或者有更好的解决方案我不知道。谢谢。

javascript error-handling callback
4个回答
5
投票

API 应记录它将调用回调同步(如Array#sort

)或异步(如
Promise#then
),然后
始终遵守记录的保证。它不应该混合搭配。

所以,是的,如果您有一个通常会异步调用回调的函数,那么它应该

始终异步调用它,无论它为何进行调用。

jQuery 中有一个很好的例子:当 jQuery 第一次添加“延迟”对象时,如果延迟已经解决,它们会

同步调用回调,但如果还没有解决,则异步调用。这是许多混乱和错误的根源,这也是为什么 ES2015 的承诺保证 then

catch
 回调将始终被异步调用的部分原因。


如果可能并且与代码库的其余部分不冲突,请考虑使用

Promises 而不是简单的回调。 Promise 为异步操作(以及与同步操作的互操作)提供了非常清晰、简单、有保证的语义和可组合性。


3
投票
异步函数的调用者应该知道调用该函数的结果是什么。对于异步函数应该返回什么有一个标准,Promises。

如果您的函数返回

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。


1
投票

不,立即回调并没有什么害处,事实上,故意延迟错误只会浪费时间和开销。是的,立即回调错误可能非常有害,对于假设的函数应该避免异步! (看那个,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 进行类型检查。 在您的情况下,请尝试以下操作:

0
投票
callback


© www.soinside.com 2019 - 2024. All rights reserved.