[Effective JavaScript 笔记]第68条:使用promise模式清洁异步逻辑
构建异步API的一种流行的替代方式是使用promise(有时也被称为deferred或future)模式。已经在本章讨论过的异步API使用回调函数作为参数。
downloadAsync('file.txt',function(file){
console.log('file:'+file);
});
基于promise的API不接收回调函数作为参数。相反,它返回一个promise对象,该对象通过其自身的then方法接收回调函数。
var p=downloadP('file.txt');
p.then(function(file){
console.log('file: '+file);
});
这里看不出与原先的版本有什么不同。但是promise的力量在于它们的组合性。传递给then方法的回调函数不仅产生影响,也可以产生结果。通过回调函数返回一个值,可以构造一个新的promise。
var fileP=downloadP('file.txt');
var lengthP=fileP.then(function(file){
return file.length;
});
lengthP.then(function(length){
console.log('length: '+length);
});
理解promise的一种方法是将它理解为表示最终值的对象。它封装了一个还未完成的并发操作,但最终会产生一个结果值。then方法允许我们提供一个代表最终值的一种类型的promise对象,并产生一个新的promise对象来代表最终值的另一种类型,而不管回调函数返回了什么。
从现有的promise中构造新promise的能力带来了很大的灵活性,并且具有一些简单但强大的惯用法。例如,构造一个实用程序来拼接多个promise的结果。
var filesP=join(downloadP('file1.txt'),
downloadP('file2.txt'),
downloadP('file3.txt'));
filesP.then(function(files){
console.log('file1:'+files[0]);
console.log('file2:'+files[1]);
console.log('file3:'+files[2]);
});
promise库也经常提供一个叫做when的工具函数,其使用类似。
var fileP1=downloadP('file1.txt'),
fileP2=downloadP('file2.txt'),
fileP3=downloadP('file3.txt');
when([fileP1,fileP2,fileP3],function(files){
console.log('file1:'+files[0]);
console.log('file2:'+files[1]);
console.log('file3:'+files[2]);
});
使promise成为卓越的抽象层级的部分原因是通过then方法的返回值来联系结果,或者通过工具函数如join来构成promise,而不是在并行的回调函数间共享数据结构。本质上是安全的,因为避免了66条中讨论过的数据竞争。即使最小心谨慎的程序员也可能会在保存异步操作的结果到共享的变量或数据结构时犯下简单的错误。
var file1,file2;
downloadAsync('file1.txt',function(file){
file1=file;
});
downloadAsync('file2.txt',function(file){
file1=file;
});
promise避免这种BUG,简单风格的组合promise避免了修改共享数据。
注意异步逻辑的有序链事实上也可用有序的promise,而不是在62条中展现的笨重的嵌套模式。错误处理会自动地通过promise传播。当你通过promise串联异步操作的集合时,你可以为整个序列提供一个简单的error回调函数,而不是将error回调函数传递给每一步,正如63条中的代码所示。
尽管这样,有时故意创建某些种类的数据竞争是有用的。Promise为些提供了一个很好的机制。例如,一个应用程序可能需要尝试从多个不同的服务器上同时下载同一份文件,而选择最先完成的那个文件。select(或choose)工具函数接收几个promise并产生一个其值是最先完成下载的文件的promise。换句话,几个promise彼此竞争。
var fileP=select(downloadP('http://e1.com/file.txt'),downloadP('http://e2.com/file.txt'),downloadP('http://e3.com/file.txt'));
fileP.then(function(file){
console.log('file: '+file);
});
select函数的另一个用途是提供超时来终止长时间的操作。
var fileP=select(downloadP('http://e1.com/file.txt'),timeoutErrorP(2000));
fileP.then(function(file){
console.log('file: '+file);
},function(error){
console.log('I/O error or timeout: '+error);
});
这里提供error回调函数作为第二个参数给promise的then方法的机制。
提示
promise代表最终值,即并行操作完成时最终产生的结果
使用promise组合不同的并行操作
使用promise模式的API避免数据竞争
在要求有意的竞争条件时使用select(也被称为choose)
扩展阅读
Promise https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise
[Effective JavaScript 笔记]第68条:使用promise模式清洁异步逻辑的更多相关文章
- [Effective JavaScript 笔记] 第4条:原始类型优于封闭对象
js有5种原始值类型:布尔值.数字.字符串.null和undefined. 用typeof检测一下: typeof true; //"boolean" typeof 2; //&q ...
- [Effective JavaScript 笔记] 第5条:避免对混合类型使用==运算符
“1.0e0”=={valueOf:function(){return true;}} 是值是多少? 这两个完全不同的值使用==运算符是相等的.为什么呢?请看<[Effective JavaSc ...
- [Effective JavaScript 笔记]第27条:使用闭包而不是字符串来封装代码
函数是一种将代码作为数据结构存储的便利方式,代码之后可以被执行.这使得富有表现力的高阶函数抽象如map和forEach成为可能.它也是js异步I/O方法的核心.与此同时,也可以将代码表示为字符串的形式 ...
- [Effective JavaScript 笔记]第28条:不要信赖函数对象的toString方法
js函数有一个非凡的特性,即将其源代码重现为字符串的能力. (function(x){ return x+1 }).toString();//"function (x){ return x+ ...
- [Effective JavaScript 笔记]第46条:使用数组而不要使用字典来存储有序集合
对象属性无序性 js对象是一个无序属性集合. var obj={}; obj.a=10; obj.b=30; 属性a和属性b并没有谁前谁后之说.for...in循环,先输出哪个属性都有可能.获取和设置 ...
- [Effective JavaScript 笔记]第45条:使用hasOwnProperty方法以避免原型污染
之前的43条,44条讨论了属性的枚举,但都没有彻底地解决属性查找中原型污染的问题.看下面关于字典的一些操作 'zhangsan' in dict; dict.zhangsan; dict.zhangs ...
- [Effective JavaScript 笔记]第67条:绝不要同步地调用异步的回调函数
设想有downloadAsync函数的一种变种,它持有一个缓存(实现为一个Dict)来避免多次下载同一个文件.在文件已经被缓存的情况下,立即调用回调函数是最优选择. var cache=new Dic ...
- [Effective JavaScript 笔记]第66条:使用计数器来执行并行操作
第63条建议使用工具函数downloadAllAsync接收一个URL数组并下载所有文件,结果返回一个存储了文件内容的数组,每个URL对应一个字符串.downloadAllAsync并不只有清理嵌套回 ...
- [Effective JavaScript 笔记]第65条:不要在计算时阻塞事件队列
第61条解释了异步API怎样帮助我们防止一段程序阻塞应用程序的事件队列.使用下面代码,可以很容易使一个应用程序陷入泥潭. while(true){} 而且它并不需要一个无限循环来写一个缓慢的程序.代码 ...
随机推荐
- Maven3路程(一)用Maven创建第一个web项目(1)
一.创建项目 1.Eclipse中用Maven创建项目 上图中Next 2.继续Next 3.选maven-archetype-webapp后,next 4.填写相应的信息,Packaged是默认创建 ...
- 原生javascript封装ajax和jsonp
在我们请求数据时,完成页面跨域,利用原生JS封装的ajax和jsonp: <!DOCTYPE html> <html lang="en"> <head ...
- [CareerCup] 17.1 Swap Number In Place 互换位置
17.1 Write a function to swap a number in place (that is, without temporary variables). 这道题让我们交换两个数, ...
- Spring和Hibernate集成配置
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.spr ...
- 如何设置div的宽度为100%-xx px?
如何设置div的宽度为100%-xx px? 参见如下帖子:http://stackoverflow.com/questions/15183069/div-width-100-10px-relativ ...
- Ajax提交后台中文乱码问题
今天项目组同事反映,之前有一个正常的请求,今天突然后台获取参数的值出现了中文乱码,怀疑是之前更新jar包所致,笔者仔细想了想更新的内容,仿佛没有涉及到编码的变更啊,然后开始排查,首先后台加了强制以ut ...
- github 离线版本下载
http://download.csdn.net/download/nevinblog/9031049
- 【iCore3 双核心板_ uC/OS-III】例程一:认识 uC/OS-III
实验指导书及代码包下载: http://pan.baidu.com/s/1i4FuMep iCore3 购买链接: https://item.taobao.com/item.htm?id=524229 ...
- HDU 4287 Intelligent IME(字典树数组版)
Intelligent IME Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) ...
- MVC4脚本压缩 BundleTable bundles 404错误
在发布网站的时候,因为使用了MVC4的新特性BundleTable,造成访问的时候js和css报了404错误, google了以后, 有朋友说是因为要在webservice添加 <modules ...