上一篇文章,我分析了同步代码做异常处理是基于责任链模式,而通过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. ASP.NET关于对excel数据导入到数据库

    //*******************************C#(UI代码)********************************/ Web界面上放一个FileUpload 名称为:F ...

  2. Java NIO6:选择器2---代码篇

    选择器服务器端代码 上一篇文章毫无条理地讲了很多和选择器相关的知识点,下面进入实战,看一下如何写和使用选择器实现服务端Socket数据接收的程序,这也是NIO中最核心.最精华的部分. 看一下代码: p ...

  3. 删除Mysql数据表中多余的重复记录的sql语句

    数据表 sniper_tb 中存在主键 id,字段url,现需要在url字段上添加 unique,但由于url存在重复记录,导致添加失败. 如何删除表中多余的url重复记录,仅保持一条? 思路一 将 ...

  4. 为jQuery添加Webkit的触摸方法支持

    前些日子收到邮件,之前兼职的一个项目被转给了其他人,跟进的人来问我相关代码的版权问题. 我就呵呵了. 这段代码是我在做13年一份兼职的时候无聊加上去的,为jQuery添加触摸事件的支持.因为做得有点无 ...

  5. Java 浅析三大特性之一多态

    Java 浅析三大特性之一多态 之前我们的文章讲了Java的封装和继承,封装讲的时候,并没有体现出来封装的强大之处,反而还要慎用封装.因为这时的封装还没有和多态联系到一起,还无法看出向上转型的厉害之处 ...

  6. iOS-----Crash文件分析(一)

    开发程序的过程中不管我们已经如何小心,总是会在不经意间遇到程序闪退.脑补一下当你在一群人面前自信的拿着你的App做功能预演的时候,流畅的操作被无情地Crash打断.联想起老罗在发布Smartisan ...

  7. java IO流 之 字符流

    字符是我们能读懂的一些文字和符号,但在计算机中存储的却是我们看不懂的byte 字节,那这就存在关于字符编码解码的问题.所以在学习Io流的字符流前我们先了解些关于编码问题. 一.字符集与字符编码 1.什 ...

  8. Exception:HTTP Status 500 - org.apache.ibatis.binding.BindingException: Invalid bound statement (not found)

    主要错误信息如下: HTTP Status 500 - org.apache.ibatis.binding.BindingException: Invalid bound statement (not ...

  9. php的mysql\mysqli\PDO(二)mysqli

    原文链接:http://www.orlion.ga/1147/ mysqli有面向对象风格和面向过程风格,个人感觉还是用面向对象风格比较好(毕竟是面向对象) 1.mysqli::_construct( ...

  10. Python标准模块--functools

    1 模块简介 functools,用于高阶函数:指那些作用于函数或者返回其它函数的函数,通常只要是可以被当做函数调用的对象就是这个模块的目标. 在Python 2.7 中具备如下方法, cmp_to_ ...