JavaScript回调错误处理

时间:2016-08-09 14:11:16

标签: javascript error-handling callback

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

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

3 个答案:

答案 0 :(得分:5)

API应记录它将同步调用回调 (如Array#sort)或异步调用(如Promise#then),然后始终遵守记录的内容保证。它不应该混合搭配。

所以,是的,如果你有一个通常会异步调用回调的函数,它应该总是异步调用它,无论它为何进行调用。

jQuery中有一个很好的例子:当jQuery首次添加“延迟”对象时,如果延迟已经解决,它们会同步调用回调,但如果没有,则异步调用异步。这是许多混淆和错误的根源,这也是ES2015承诺保证thencatch回调始终被异步调用的原因之一。

如果可能且与代码库的其余部分不相符,请查看使用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,异步函数的调用者将不必担心您在函数内部的实现,并且您可以直接抛出错误或拒绝承诺。