管理异步编程的一个是错误处理。同步代码中只要使用try语句块包装一段代码很容易一下子处理所有的错误。

try{
f();
g();
h();
} catch(e){
//这里用来下得出现的错误
}

try语句块

但对于异步的代码,多步的处理通常会被分隔到事件队列的单独轮次中,因此,不可能将它们包装在一个try语句块中。事实上异步的API甚至根本不可能抛出异常,因为,当一个异步的错误发生时,没有一个明显的执行上下文来抛出异常!相反,异步的API倾向于将错误表示为回调函数的特定参数,或使用一个附加的错误处理回调函数。例如,一个涉及下载文件的异步API可能会有一个额外的回调函数来处理网络错误。

downloadAsync('http://cnblogs.com/wengxuesong',function(text){
console.log('file contents:'+text);
},function(error){
console.log('error:'+error);
});

如果下载多个文件,可以像62条讲的,使用回调函数嵌套起来。

downloadAsync('a.txt',function(a){
downloadAsync('b.txt',function(b){
downloadAsync('c.txt',function(c){
console.log('Contents:'+a+b+c);
},function(error){
console.log('error:'+error);
});
},function(error){
console.log('error:'+error);
});
},function(error){
console.log('error:'+error);
});

上面代码上,每一步的处理都使用了相同的错误处理逻辑,然而我们在多个地方重复了相同的代码。在编程领域里,应该努力坚持避免重复代码。通过共享作用域中定义一个错误处理的函数,将重复代码抽象出来。

function onError(error){
console.log('Error:'+error);
}
downloadAsync('a.txt',function(a){
downloadAsync('b.txt',function(b){
downloadAsync('c.txt',function(c){
console.log('Contents:'+a+b+c);
},onError);
},onError);
},onError);

如果使用工具函数downloadAllAsync将多个步骤合并到一个复合的操作中,那么,只需要提供一个错误处理的回调函数。

downloadAllAsync(['a.txt','b.txt','c.txt'],function(abc){
console.log('Contents:'+abc[0]+abc[1]+abc[2]);
},function(error){
console.log('Error:'+error);
});

回调函数错误参数

另一种错误处理API的风格是Node.js平台使用的。该风格只需要一个回调函数,该回调函数的第一个参数如果有错误发生就表示一个错误。否则就是一个假值,比如null。对于这类API,我们可以定义一个通用的错误处理函数,需要使用if语句来控制每个回调函数。

function onError(error){
console.log('Error:'+error);
}
downloadAsync('a.txt',function(error,a){
if(error){
onError(error);
return;
}
downloadAsync('b.txt',function(error,b){
if(error){
onError(error);
return;
}
downloadAsync('c.txt',function(error,c){
if(error){
onError(error);
return;
}
console.log('Contents:'+a+b+c);
});
});
});

程序员通常会放弃if语句而使用大括号结构跨越多行的约定,以使得错误处理更简洁、更集中。

function onError(error){
console.log('Error:'+error);
}
downloadAsync('a.txt',function(error,a){
if(error)return onError(error);
downloadAsync('b.txt',function(error,b){
if(error)return onError(error);
downloadAsync('c.txt',function(error,c){
if(error)return onError(error);
console.log('Contents:'+a+b+c);
});
});
});

也可以使用一个抽象合并步骤来帮助消除重复

var filenames=['a.txt','b.txt','c.txt'];
downloadAllAsync(filenames,function(error,abc){
if(error){
console.log('Error:'+error);
return;
}
console.log('Contents:'+abc[0]+abc[1]+abc[2]);
});

try...catch语句和在异步API中典型的错误处理逻辑的一个实际差异是,try语句使得定义一个"捕获所有"的逻辑很容易导致程序员难以忘怀整个代码区的错误处理。而上面给出的异步API,非常容易忘记在进程的任意一步提供错误处理。这将导致错误被丢弃。忽视错误处理的程序会令用户非常沮丧:应用程序出错时没有任何的反馈。类似的,默认的错误不好调试。因为没有提供问题来源的线索。最好是做好防御,即使用异步API需要警惕,确保明确地处理所有的错误状态条件。

提示

  • 通过编写共享的错误处理函数来避免复制和粘贴错误处理代码

  • 确保明确地处理所有的错误条件以避免丢弃错误

[Effective JavaScript 笔记]第63条:当心丢弃错误的更多相关文章

  1. [Effective JavaScript 笔记] 第4条:原始类型优于封闭对象

    js有5种原始值类型:布尔值.数字.字符串.null和undefined. 用typeof检测一下: typeof true; //"boolean" typeof 2; //&q ...

  2. [Effective JavaScript 笔记] 第5条:避免对混合类型使用==运算符

    “1.0e0”=={valueOf:function(){return true;}} 是值是多少? 这两个完全不同的值使用==运算符是相等的.为什么呢?请看<[Effective JavaSc ...

  3. [Effective JavaScript 笔记]第28条:不要信赖函数对象的toString方法

    js函数有一个非凡的特性,即将其源代码重现为字符串的能力. (function(x){ return x+1 }).toString();//"function (x){ return x+ ...

  4. [Effective JavaScript 笔记]第27条:使用闭包而不是字符串来封装代码

    函数是一种将代码作为数据结构存储的便利方式,代码之后可以被执行.这使得富有表现力的高阶函数抽象如map和forEach成为可能.它也是js异步I/O方法的核心.与此同时,也可以将代码表示为字符串的形式 ...

  5. [Effective JavaScript 笔记]第68条:使用promise模式清洁异步逻辑

    构建异步API的一种流行的替代方式是使用promise(有时也被称为deferred或future)模式.已经在本章讨论过的异步API使用回调函数作为参数. downloadAsync('file.t ...

  6. [Effective JavaScript 笔记]第66条:使用计数器来执行并行操作

    第63条建议使用工具函数downloadAllAsync接收一个URL数组并下载所有文件,结果返回一个存储了文件内容的数组,每个URL对应一个字符串.downloadAllAsync并不只有清理嵌套回 ...

  7. [Effective JavaScript 笔记]第15条:当心局部块函数声明笨拙的作用域

    嵌套函数声明.没有标准的方法在局部块里声明函数,但可以在另一个函数的顶部嵌套函数声明. function f(){return "global"} function test(x) ...

  8. [Effective JavaScript笔记]第3条:当心隐式的强制转换

    js对类型错误出奇的宽容 3+true;  //4 3*””;  //0 3+[]; //3 3+[3]; //33 以上表达式在许多语言早就变红了.而js不但不报错还给你个结果. 极少情况会产生即时 ...

  9. [Effective JavaScript 笔记] 第14条:当心命名函数表达式笨拙的作用域

    js函数会根据上下文改变其含义. function double(x){return x*2;} 这是一个函数声明,也可以是一个命名函数表达式(named function expression),取 ...

随机推荐

  1. 用GruntJS合并、压缩JS文件

    为什么要合并.压缩你的JS文件?        一个项目开发完成我们总能发现有一堆js文件非常混乱.           一般在一个HTML文档加载的时候,浏览器会根据HTML代码从上到下读取所需要加 ...

  2. Hadoop.2.x_HA部署

    一.概念与HA思路 1. 首先Hadoop架构为主从架构(NameNode/DataNode) 2. NameNode管理着文件系统和与维护客户端访问DataNode 3. Hadoop 2.0 之前 ...

  3. CentOS7配置双网卡绑定

    配置team0配置文件: [root@CentOS7 ~]# cat /etc/sysconfig/network-scripts/ifcfg-team0DEVICE=team0DEVICETYPE= ...

  4. 获取行间样式与在js中设置样式

    !DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/x ...

  5. Jfinal验证码功能

    //验证码工具类 import java.awt.Color;import java.awt.Font;import java.awt.Graphics;import java.awt.image.B ...

  6. HDU5831

    Rikka with Parenthesis II Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Jav ...

  7. sql*loader的直接加载方式和传统加载方式的性能差异

    1.确认数据库版本 2.数据准备 3.创建导入表及控制文件 4.直接加载方式演示 查看具体的日志: 5.传统加载方式演示 查看日志文件: 6.结论及两种方式的差异 经过比对direct比convent ...

  8. vbox下Oracle Enterprise liunx5.4虚拟机安装10G RAC实验(一)

    1.配置第一个虚拟机 1.1 安装后的登录界面 1.2 第1台机器(单数据配置方面) 1.2.1 验证安装包 1.2.2 修改内核参数 1.2.3添加安全限制 1.2.4关闭防火墙 1.2.5添加用户 ...

  9. SQLiteDatabase浅谈

    (一).简介: Android通过 SQLite 数据库引擎来实现结构化数据的存储.在一个数据库应用程序中,任何类都可以通过名字对已经创建的数据库进行访问,但是在应用程序之外就不可以. SQLite  ...

  10. Combination Lock

    时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 Finally, you come to the interview room. You know that a Micr ...