上一篇文章,我分析了同步代码做异常处理是基于责任链模式,而通过try、catch等语句可以很容易地实现这种责任链模式。但是如果是异步调用,我们无法直接通过try、catch语句实现责任链模式,并且通过一个demo证明使用回调函数的方式去实现去实现异常处理的责任链模式是非常繁琐而且代码难以规范的,适用性不高。有没有什么方式能够使得异步js的责任链模式能够更加简单地实现呢?

对于这个问题,我们还是先回到js异步调用上,随着node和npm的普及,js异步调用也越来越被大家重视,于是乎npm引用了一个叫promise的设计模式,立刻引起轰动效应,一时间各个js类库纷纷引入这个设计模式,代替原有的js回调模式。promise后来也被官方接受,成了es6引入的新语法糖之一。因为promise是基于设计模式的,所以promise是可以在任何浏览器上实现出来的,因此很多的框架都有promise的影子,比如jq的$.deferred、angular的promise,另外在ext、dojo里也有对这个模式的封装,可以说目前流行的各大框架,都有promise的实现,由此可见我们没有理由不采用promise来封装我们异步调用。

一、promise的异步调用

还是先来看看promise,promise对象来自对promise/A+规范的实现,而这个规范本身就可以看成是对一种异步编程的设计模式。关于promise不想在这里涉及太多,因为早就有很多人将他将了多少遍了,这里给出一个实现上一篇文章的例子代码便可。

function fa(){
return new Promise(function (resolve, reject) {
setTimeout(function(){
//执行异步方法a的内容
resolve();
})
}).then(function(){
//调用异步方法b
return fb();
})
}
function fb(){
return new Promise(function (resolve, reject) {
setTimeout(function(){
//执行异步方法b的内容
resolve();
})
}).then(function(){
//调用异步方法c
return fc();
})
}
function fc(){
return new Promise(function (resolve, reject) {
setTimeout(function(){
//执行异步方法c的内容
resolve();
})
})
}
fa();

其中,每个异步过程可以简写为

function fa(){
return new Promise(function (resolve, reject) {
setTimeout(function(){
//执行异步方法a的内容
//调用异步方法b
resolve(fb());
})
})
}

二、promise的于异常处理

参考promise/A+规范(https://promisesaplus.com/),从2.2.7开始看,有:

promise2 = promise1.then(onFulfilled, onRejected)

这里的onFulfilled和onRejected分别对应promise的两个结果状态fulfilled、rejected,也就是回调。这个onFulfilled指的是成功状态的调用函数,而这个onRejected指的是错误状态的回调。同时then函数的返回值也是一个promise,我们把它称为promise2,于是有:

    • If either onFulfilled or onRejected returns a value x, run the Promise Resolution Procedure[[Resolve]](promise2, x).
    • If either onFulfilled or onRejected throws an exception epromise2 must be rejected with eas the reason.
    • If onFulfilled is not a function and promise1 is fulfilled, promise2 must be fulfilled with the same value as promise1.
    • If onRejected is not a function and promise1 is rejected, promise2 must be rejected with the same reason as promise1.

从第一条可以看出,如果promise1是rejected态的,但是onRejected返回了一个值(包括undifined),那么promise2还是fulfilled态的,这个过程相当于catch到异常,并将它处理掉,所以不需要向上抛出。

从第二条可以看出,如果promise1本身是rejected态的,或者promise1是fulfilled态但是onFulfilled和onRejected出现了异常,promise2也会是rejected态的,并且会获得promise1的被拒绝的原因,或者是异常。

从第四条可以看出,如果promise1是rejected态的,并且没有定义onRejected,则promise2也会是rejected态的。

还记得我们曾经说过

function f(){
try{
} catch(e){
throw e;
}
} //等效于
function f(){}

对promise是一样的,如果的rejected态的,并且未定义onRejected,则被拒绝的原因或异常会继续向上抛出,抛给promise2,这不就是我们想要的异常处理的责任链模式的语法糖吗?即有

return new Promise(function (resolve, reject) {
resolve();
}).then(function(){
throw new Error()
}).then(null,function(err){
//什么也不做
throw err;
})
//等效于
return new Promise(function (resolve, reject) {
resolve();
}).then(function(){
throw new Error()
})

结合之前的a、b、c的三个异步过程的调用,我们可以完善代码为。

function fa(){
return new Promise(function (resolve, reject) {
setTimeout(function(){
//执行异步方法a的内容
resolve();
})
}).then(function(){
//调用异步方法b
return fb();
}).catch(function(e){
if(e == "方法a能处理的异常"){
console.log("方法a处理了异常")
} else {
console.log("方法a无法处理此异常,继续向上抛出")
throw e;
}
})
}
function fb(){
return new Promise(function (resolve, reject) {
setTimeout(function(){
//执行异步方法b的内容
resolve();
})
}).then(function(){
//调用异步方法c
return fc();
}).catch(function(e){
if(e == "方法b能处理的异常"){
console.log("方法b处理了异常")
} else {
console.log("方法b无法处理此异常,继续向上抛出")
throw e;
}
})
}
function fc(){
return new Promise(function (resolve, reject) {
setTimeout(function(){
resolve();
})
}).then(function(){
throw "方法acb都不能处理此异常";
//throw "方法a能处理的异常";
//throw "方法b能处理的异常";
//throw "方法c能处理的异常";
}).catch(function(e){
if(e == "方法c能处理的异常"){
console.log("方法c处理了异常")
} else {
console.log("方法c无法处理此异常,继续向上抛出")
throw e;
}
})
}
fa().catch(function(e){
console.log("最顶层处理了此异常");
});

这里的catch就相当于then(null,onRejected)。

当然如果a、b、c都没有可以处理的异常,则上面的代码就简化为第一节中的代码。

从测试结果可以看出,promise的异常处理也遵循责任链模式。因此用这种模式调用异步过程,异常的处理方式是类似于传统过程式调用的异常的处理方式的。同时这种调用方式是E6规范中的,是js未来的发展方向,以此规范封装js的异步回调,是适应js发展方向的。

总结

我们通过引入promise这种异步调用模型,解决了异步调用不能使用责任链模式的问题。除了异步过程的js的函数调用情况,如函数柯里化、惰性函数都适用于传统try、catch语法,这里不再演示。

将异步过程调用过程解决后,那么基本上js的函数调用的异常处理都可以适用于责任链模式。既然技术上没有问题了,那么下一章将分享我们统一异常处理的设计方案。

js构建ui的统一异常处理方案(二)的更多相关文章

  1. js构建ui的统一异常处理方案(三)

    笔者之前分析了如何实现js的责任链异常处理的方法,通过promise这个异步模型,我们能够对同步方法和异步方法的两种情况,均可以实现责任链模式.有了这些武器,我们就可以开始设计ui的统一异常处理方案了 ...

  2. js构建ui的统一异常处理方案(四)

    上一篇我们介绍了统一异常处理方案的设计方案,这一篇我们将直接做一个小例子,验证我们的设计方案. 例子是一个todo的列表界面(页面代码参考于https://github.com/zongxiao/Dj ...

  3. js构建ui的统一异常处理方案(一)

    从早期从事基于java的服务器端开发,再到之后从事基于web和js的ui开发,总体感觉基于web页面的ui开发远不如服务器端健壮.主要是早期ie浏览器功能太弱小,很多业务被迫放到服务器端去实现,浏览器 ...

  4. Web应用的统一异常处理(二十四)

    我们在做Web应用的时候,请求处理过程中发生错误是非常常见的情况.Spring Boot提供了一个默认的映射:/error,当处理中抛出异常之后,会转到该请求中处理,并且该请求有一个全局的错误页面用来 ...

  5. Spring Boot统一异常处理方案示例

    一.异常处理的原则 1.调用方法的时候返回布尔值来代替返回null,这样可以 NullPointerException.由于空指针是java异常里最恶心的异常. 2. catch块里别不写代码.空ca ...

  6. 【Spring Boot】Spring Boot之统一异常处理

    一.统一异常处理的作用 在web应用中,请求处理时,出现异常是非常常见的.所以当应用出现各类异常时,进行异常的统一捕获或者二次处理(比如空指针异常或sql异常正常是不能外抛)是非常必要的,然后右统一异 ...

  7. Redis总结(五)缓存雪崩和缓存穿透等问题 Web API系列(三)统一异常处理 C#总结(一)AutoResetEvent的使用介绍(用AutoResetEvent实现同步) C#总结(二)事件Event 介绍总结 C#总结(三)DataGridView增加全选列 Web API系列(二)接口安全和参数校验 RabbitMQ学习系列(六): RabbitMQ 高可用集群

    Redis总结(五)缓存雪崩和缓存穿透等问题   前面讲过一些redis 缓存的使用和数据持久化.感兴趣的朋友可以看看之前的文章,http://www.cnblogs.com/zhangweizhon ...

  8. spring boot / cloud (二) 规范响应格式以及统一异常处理

    spring boot / cloud (二) 规范响应格式以及统一异常处理 前言 为什么规范响应格式? 我认为,采用预先约定好的数据格式,将返回数据(无论是正常的还是异常的)规范起来,有助于提高团队 ...

  9. Java springmvc 统一异常处理的方案

    前言:为什么要统一异常处理?经常在项目中需要统一处理异常,将异常封装转给前端.也有时需要在项目中统一处理异常后,记录异常日志,做一下统一处理. Springmvc 异常统一处理的方式有三种. 一.使用 ...

随机推荐

  1. bat 延时删除指定文件夹中的文件经验分享

    1.bat延时 xp程序中通过ping 127.0.0.1 -n 20 来实现延时操作,ping本地地址20行. win7中通过timeout 20 来实现延时20秒. 2.删除指定文件 del /q ...

  2. HTML学习有感

    自从到大三之后一直在纠结课下去学些什么,刚开始一直在学PS,当时学的还算可以,可以一段时间不用之后就忘记了,这使我很郁闷!之后一直想学JAVA,跟已经工作的同学讨来了相关的视屏资料以及他培训时的笔记: ...

  3. Android+jsp +html 文件上传案例 已测试 成功通过

    我文件上传一直是广大读者一个问题 今天就把成功案例写下 javaweb 网页前段 <%@ page language="java" import="java.uti ...

  4. 黑科技:gif二维码

    本篇文章是缘于在微博上看到了一的有意思的东西.由于腾讯与阿里的竞争关系,如果你是一个大V,在微博上发布微信的二维码会被屏蔽掉.于是有人发现了这样一个现象:人眼有视觉暂留效应,手机的摄像头由于捕捉影像的 ...

  5. 在C#代码中应用Log4Net(二)典型的使用方式

    不管用什么框架,学什么东西,最初的想法还不是尽快地用上这个框架,所以我们在这个章节还是不打算介绍具体配置节的应用,而是直接给出一个经典的使用样例,让你尽快上手.即使你对Log4Net的配置不熟悉也完全 ...

  6. 毫秒级的时间处理上G的图片(生成缩略图)

    测试环境: 测试图片(30M): 测试计时方法: Stopwatch sw1 = new Stopwatch(); sw1.Start(); //TODO...... sw1.Stop(); stri ...

  7. Intro to CSS 3D transforms

    原文地址:Intro to CSS 3D transforms,本文只是翻译了其中的一部分,省去了作者写文章的原因浏览器兼容部分(已经过时) Perspective 元素需要设置需要设置perspec ...

  8. [ASP.NET MVC 小牛之路]08 - Area 使用

    ASP.NET MVC允许使用 Area(区域)来组织Web应用程序,每个Area代表应用程序的不同功能模块.这对于大的工程非常有用,Area 使每个功能模块都有各自的文件夹,文件夹中有自己的Cont ...

  9. java.lang.IndexOutOfBoundsException at java.io.FileOutputStream.writeBytes(Native Method)

    ss available : /usr/linkapp/data/linkapp/ddn_1440639847758_temp java.lang.IndexOutOfBoundsException ...

  10. Java中常见的29个运行异常

    ArithmeticException, ArrayStoreException, BufferOverflowException, BufferUnderflowException, CannotR ...