上一篇文章,我分析了同步代码做异常处理是基于责任链模式,而通过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. CentOS6.5安装中文输入法

    首先进入命令形式的客户端 切换成root用户,输入命令"su root"即可,接着输入 yum install "@Chinese Support" 命令按en ...

  2. phpcms v9常用方法

    1.联动菜单根据地区id显示地区名称的方法: 显示效果: 四川 >> 攀枝花 >> 仁和区 [字段名字为 area] {get_linkage($info['area'],1, ...

  3. 一鼓作气 博客--第三篇 note3

    1 推荐读书消费者行为学 -商业的本质,APP得到,5分钟商学院 2定义字典 dic={'name':haibao,'age':18} 3字典的基本操作--查询 dic={'name':'haibao ...

  4. Mac OS X上IntelliJ IDEA 13与Tomcat 8的Java Web开发环境搭建

    这标题实在有点拗口,不知道怎么写好,但看了标题也就明白文本的内容.最近几天在折腾这些玩意儿,所以写写总结.除了环境搭建,本文还是一篇入门级的上手教程. 去下载一些东西 JDK安装 Tomcat安装 T ...

  5. SQL Server 2012 Managed Service Account

    原创地址:http://www.cnblogs.com/jfzhu/p/4007472.html 转载请注明出处 (一)Windows服务使用的登陆帐号 Windows服务只有登录到某一帐户的情况下才 ...

  6. EF:The provider did not return a ProviderManifest instance

    报告错误1:指定的存储区提供程序在配置中找不到,或者无效. 报告错误2:System.Data.ProviderIncompatibleException: The provider did not ...

  7. Elasticsearch查询——布尔查询Bool Query

    Elasticsearch在2.x版本的时候把filter查询给摘掉了,因此在query dsl里面已经找不到filter query了.其实es并没有完全抛弃filter query,而是它的设计与 ...

  8. 删除 Windows 旧 OS 加载器

    装过多个系统,然后又删除掉了,系统启动引导时,又把以前的废弃的系统引导给带了出来,试过多种方式,以下方法是最好的. 开始->运行->cmd bcdedit /v 查看要删除的"W ...

  9. c#运算表达式

    1.取补运算 操作符:~ 操作数:限定int,uint,long,ulong和枚举类型,返回值于操作数类型相同 sbyte,byte,short,ushort,也可以运算,但运算前都将隐式转换为int ...

  10. myeclipse转到函数定义的方法去

    转到函数的定义CTRl+鼠标左击 myeclipse自动补全的快捷键 alt+/