上一篇文章,我分析了同步代码做异常处理是基于责任链模式,而通过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. Java环境设置

    win7/win8下JDK环境变量设置方法 首先需要到官网上下载JDK这款软件,本人下载的是jdk-7u40-windows-i586版本,安装完成显示jdk1.7.0_67. 其次选择安装路径.本人 ...

  2. canvas绘制坐标轴

    效果图如下, var canvas = document.getElementById("canvas"), context = canvas.getContext("2 ...

  3. 反向传播(BP)算法

    著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处.作者:刘皮皮链接:https://www.zhihu.com/question/24827633/answer/29120394来源 ...

  4. SQL Azure (16) 创建PaaS SQL Azure V12数据库

    <Windows Azure Platform 系列文章目录> 本文介绍的是国内由世纪互联运维的Azure China 最新的Azure PaaS SQL Database(SQL Azu ...

  5. ls /usr/linkapp 没反应

    ls /usr/linkapp ll /usr/linkapp  都是一样无反应 没有任何反应, ctrl + c /  ctrl + d 都不行 但是 ls /usr/linkapp/ | wc - ...

  6. iOS开发——高级技术&本地化与国际化详解

    本地化与国际化详解 效果如下:   英语:                                                                    中文: 具体实现如下: ...

  7. JS中script词法分析

    核心:JS中的script是分段执行的. <script> var i = 10; </script> <script> alert(i); </script ...

  8. ATM模拟器(附代码及运行结果)

    源代码: import java.util.Scanner; class Account{ String identify; String name; String date; String key; ...

  9. 浅谈ThinkPHP3.2的子域名部署和路由优化(一)

    前言:建立一个网站系统,往往包含多个子网站,例如PC官网,移动端官网,后台管理,数据源自一个相同的数据库,整个架构上,从ThinkPHP来看,可以大体理解为Model(M)是一样的,Controlle ...

  10. 数据结构与算法JavaScript (三) 链表

    我们可以看到在javascript概念中的队列与栈都是一种特殊的线性表的结构,也是一种比较简单的基于数组的顺序存储结构.由于javascript的解释器针对数组都做了直接的优化,不会存在在很多编程语言 ...