简体   繁体   English

JavaScript回调错误处理

[英]JavaScript Callback Error Handling

It's common to validate arguments and return error in functions. 验证参数并在函数中返回错误是很常见的。

However, in JavaScript callback function, such as: 但是,在JavaScript回调函数中,例如:

function myFunction(num, callback) {
  if (typeof num !== 'number') return callback(new Error('invalid num'))
  // do something else asynchronously and callback(null, result)
}

I wrote a lot of functions like this, but I wonder if there's something potentially harmful. 我写了很多这样的函数,但我想知道是否存在潜在有害的东西。 Because in most cases, the caller assumes this is an asynchronous function and the callback will execute after the code right after the function call. 因为在大多数情况下,调用者假定这是一个异步函数,并且回调将在函数调用之后的代码之后执行。 But if some arguments are invalid, the function will immediately call the callback. 但是,如果某些参数无效,该函数将立即调用回调。 So the caller must be careful dealing with the situation, that is, an unexpected execution sequence. 因此,调用者必须谨慎处理这种情况,即意外的执行序列。

I want to hear some advice on this issue. 我想听听有关此问题的一些建议。 Should I carefully assume that all asynchronous callbacks may be executed immediately? 我是否应该仔细假设所有异步回调都可以立即执行? Or I should use something like setTimeout(..., 0) to convert synchronous thing to asynchronous one. 或者我应该使用setTimeout(...,0)之类的东西将同步事物转换为异步事物。 Or there's a better solution I don't know. 还是有我不知道的更好的解决方案。 Thanks. 谢谢。

An API should document that it will call the callback either synchronously (like Array#sort ) or asynchronously (like Promise#then ), and then always obey that documented guarantee . 一个API应当记录,它会调用回调同步(如Array#sort )或异步(如Promise#then ),然后总是服从,所记录的保证 It shouldn't mix-and-match. 它不应该混搭。

So yes, if you have a function that will normally call a callback asynchronously, it should always call it asynchronously, regardless of why it's making the call. 所以是的,如果您有一个通常会异步调用回调的函数,则无论调用原因为何,它都应始终异步调用它。

There was a great example in jQuery: When jQuery first added "deferred" objects, they would call the callback synchronously if the deferred had already been settled, but asynchronously if it hadn't. jQuery中有一个很好的例子:当jQuery第一次添加“ deferred”对象时,如果已解决延迟,则将同步调用回调,否则将异步调用。 This was the source of a lot of confusion and bugs, which is part of why ES2015's promises guarantee that then and catch callbacks will always be called asynchronously. 这是很多混乱和错误的根源,这也是为什么ES2015的承诺保证thencatch回调将始终被异步调用的原因之一。


If possible and not at odds with the rest of the codebase, look at using Promises rather than simple callbacks. 如果可能,并且与其余代码库保持一致,请考虑使用Promises而不是简单的回调。 Promises provide very clear, simple, guaranteed semantics and composability for asynchronous operations (and interoperation with synchronous ones). 承诺为异步操作(以及与同步操作的互操作)提供了非常清晰,简单,有保证的语义和可组合性。

No, calling back immediately is not harmful, and in fact intentionally delaying the error just wastes time and overhead. 不,立即回叫无害,事实上,故意延迟错误只会浪费时间和开销。 Yes, calling back immediately for an error can be very harmful and should be a avoided for a function that is assumed to be asynchronous! 是的,立即调用错误可能是非常有害的,应该避免使用异步函数! (Look at that, a 180!) (看看,是180!)

From a developer's perspective, there are multiple good reasons for why set up can only be done after. 从开发人员的角度来看,为何只能在之后进行设置,有很多充分的理由。 For example here : 例如这里

const server = net.createServer(() => {}).listen(8080);

server.on('listening', () => {});

The listening event isn't attached until after .listen(8080) is invoked, because the event source is returned from the call to .listen() . 直到调用.listen(8080)之后, listening事件才被附加,因为事件源是从对.listen()的调用返回的。 In this case, implementing the listening event to invoke synchronously after .listen() is executed will be unsuccessful. 在这种情况下,实现listening事件以在执行.listen()之后同步调用将不会成功。

Here's another case I'd like to present: 这是我想介绍的另一种情况:

var num = '5';

myFunction(num, function callback(err, result) {
  if (err) {
    return myFunction(num, callback);
  }

  // handle result
});

Now, if you callback with the error synchronously, this control flow will result in a stackoverflow. 现在,如果您同步地用错误callback ,则此控制流将导致stackoverflow。 While this is the developer's fault, a stackoverflow is a really bad thing to occur from a function that's expected to be asynchronous. 尽管这是开发人员的错,但是从期望是异步的函数中发生堆栈溢出实在是一件很糟糕的事情。 This is one advantage of using setImmediate() to pass the error instead of executing the callback immediately. 这是使用setImmediate()传递错误而不是立即执行callback优点之一。

The caller of your asynchronous function should know what's going to be the result of invoking the function. 异步函数的调用者应该知道调用该函数的结果。 There is a standard for what an asynchronous function should return, Promises. 对于异步函数应返回的内容,有一个标准。

If your function returns a Promise , anybody can easily understand what's going on in that function. 如果您的函数返回Promise ,那么任何人都可以轻松了解该函数的运行情况。 Promises have the reject callback, but we could argue if the validation of the parameters should be handled by rejecting the promise or if an exception should be thrown straight forward. 承诺具有拒绝回调,但是我们可以争论是否应该通过拒绝承诺来处理参数的验证,或者是否应该直接抛出异常。 Either way, if the caller handles exceptions properly using the catch method, both directly thrown exceptions and rejects will be captured in the same manner. 无论哪种方式,如果调用者使用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); });

Both patterns will result in the error being catched and logged. 两种模式都将导致捕获并记录错误。

If you use promises, the caller of your asynchronous function will not have to worry about your implementation inside the function, and you can either throw the error straight foward or reject the promise as you please. 如果使用promise,则异步函数的调用方将不必担心函数内部的实现,并且可以直接抛出错误,也可以根据需要拒绝promise。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM