同步编程
关于错误处理这部分耗子叔写的还是很有干货的,原始文章链接
错误返回码和异常捕捉
错误码:在C、C++、Go中会使用函数的返回错误码的方式来作为异常标识
异常捕捉处理:在Java中使用 try - catch - finally
这个编程模式,不过在异步编程里,try 语句块里的函数运行在另外一个线程中,其中抛出的异常无法在调用者的这个线程中被捕捉,所以异步的代码不能使用这种处理。
作为Java后台的业务处理,目前了解到的实践方式是需要结合业务场景来混合使用这两种错误处理机制,后台业务用异常捕获,返回前端的API用状态码(参考的华为云API),http状态码(4xx/5xx等)、错误code
(比如: Ecs.0000)、错误message
(request body is illeagal),这些有错误字典的信息能给到前台很好的反馈。
异步编程
在异步编程的世界里,因为被调用的函数是被放到了另外一个线程里运行,所以无法用返回码和抛异常的普通方式来处理错误了,所以有其他的处理方式。
-
JavaScript 异步编程的错误处理
常用CallBack方式,在做异步请求的时候,注册几个 OnSuccess()、 OnFailure() 这样的函数,让在另一个线程中运行的异步代码来回调过来:function successCallback(result) { console.log("It succeeded with " + result); } function failureCallback(error) { console.log("It failed with " + error); } doSomething(successCallback, failureCallback);
但是, 如果我们需要把几个异步函数顺序执行的话,嵌套的写法混乱且不可维护,俗称回调地狱(Callback Hell):
一般来说,在异步编程的实践里,我们会用 Promise(承诺,表示我异步操作完成后将会处理成功结果或者失败结果) 模式来处理,
Promise.then().catch()的链式处理让异步处理逻辑清晰,且保证了程序的顺序执行(注意:在JavaScript中,一般情况下对于异步请求不会等到你的请求结果回来才继续向下执行,而是直接执行下面一行代码了,异步的结果会在未来某个时刻执行!!!这个跟人们普通认为的程序应该按照书写顺序从上向下执行相违背
),每一个Promise可以定义成功回调resolve()和失败回调reject(),成功后处理resolve,然后调用下一个的then,失败则reject,然后调用catch,在ECMAScript 2017中,我们可以使用async/await
这两个关键字来取代 Promise 对象,这样可以让我们的代码更易读,而且可以等待多个异步请求的结果再向下处理。用函数式编程结合语法糖甚至让代码变得异常简洁:async function foo() { try { for (let f of [func1, func2]) { await f(); } console.log(`do something else`); } catch(error) { failureCallback(error); } }
关于回调地狱、Promise、async/await的更多参考资料,可以参考如下:
- 回调地狱
- Promise[MDN权威]
- async/await[阮一峰的ES6 Blog]
-
Java异步编程的错误处理
在 Java 中,在 JDK 1.8 里也引入了类似 JavaScript 的玩法 —— CompletableFuture。这个类提供了大量的异步编程中 Promise 的各种方式。链式处理:CompletableFuture.supplyAsync(this::findReceiver) .thenApply(this::sendMsg) .thenAccept(this::notify);
supplyAsync() 表示执行一个异步方法,而 thenApply() 表示执行成功后再串联另外一个异步方法,最后是 thenAccept() 来处理最终结果。
异常处理:// 输入: "ILLEGAL" CompletableFuture.supplyAsync(Integer::parseInt) .thenApply(r -> r * 2 * Math.PI) .thenApply(s -> "apply>> " + s) .exceptionally(ex -> "Error: " + ex.getMessage());
-
错误处理的最佳实践
- 统一分类的错误字典(HTTP 的 4XX 表示客户端有问题,5XX 则表示服务端有问题)
- 同类错误的定义最好是可以扩展的(面向对象的继承)
- 定义错误的严重程度(错误日志分级别Fatal、error、Warning、Info、Debug、Trace)
- 错误日志的输出最好使用错误码,而不是错误信息(推荐使用像PageNotFound这样的标识)
- 为你的错误定义提供清楚的文档以及每种错误的代码示例(如果你是做 RESTful API 方面的,使用 Swagger 会帮你很容易搞定这个事)
- 对于异步的方式,推荐使用 Promise 模式处理错误
- 对于分布式的系统,推荐使用 APM 相关的软件(尤其是使用 Zipkin 这样的服务调用跟踪的分析来关联错误,也可以用SkyWalking)