构建异步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模式清洁异步逻辑的更多相关文章

  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 笔记]第27条:使用闭包而不是字符串来封装代码

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

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

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

  5. [Effective JavaScript 笔记]第46条:使用数组而不要使用字典来存储有序集合

    对象属性无序性 js对象是一个无序属性集合. var obj={}; obj.a=10; obj.b=30; 属性a和属性b并没有谁前谁后之说.for...in循环,先输出哪个属性都有可能.获取和设置 ...

  6. [Effective JavaScript 笔记]第45条:使用hasOwnProperty方法以避免原型污染

    之前的43条,44条讨论了属性的枚举,但都没有彻底地解决属性查找中原型污染的问题.看下面关于字典的一些操作 'zhangsan' in dict; dict.zhangsan; dict.zhangs ...

  7. [Effective JavaScript 笔记]第67条:绝不要同步地调用异步的回调函数

    设想有downloadAsync函数的一种变种,它持有一个缓存(实现为一个Dict)来避免多次下载同一个文件.在文件已经被缓存的情况下,立即调用回调函数是最优选择. var cache=new Dic ...

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

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

  9. [Effective JavaScript 笔记]第65条:不要在计算时阻塞事件队列

    第61条解释了异步API怎样帮助我们防止一段程序阻塞应用程序的事件队列.使用下面代码,可以很容易使一个应用程序陷入泥潭. while(true){} 而且它并不需要一个无限循环来写一个缓慢的程序.代码 ...

随机推荐

  1. Big Chocolate

    Big Chocolate 题目链接:http://acm.hust.edu.cn/vjudge/problem/visitOriginUrl.action?id=19127 Big Chocolat ...

  2. php中提示Undefined index的解决方法

    我们经常接收表单POST过来的数据时报Undefined index错误,如下: $act=$_POST['action']; 用以上代码总是提示 Notice: Undefined index: a ...

  3. 讨论一下js获取响应中后台传回来的BigInteger类型的数字时,后几位会自动变为0的问题

    后台返回的json:{"data":12345678912345678912} 在js中获取该data得到的值为:12345678912345680000 后经过实验发现,只有数字 ...

  4. 2016HUAS暑假集训训练2 A - Is It A Tree?

    Description A tree is a well-known data structure that is either empty (null, void, nothing) or is a ...

  5. # 20145334 《Java程序设计》第9周学习总结

    20145334 <Java程序设计>第9周学习总结 教材学习内容总结 第十六章 整合数据库 JDBC 1.Java语言访问数据库的一种规范,是一套API. 2.JDBC (Java Da ...

  6. BizTalk开发系列(二十六) 使用Web Service

    Web Service是在构建SOA平台中广泛使用的技术.在BizTalk开发过程中使用SOAP适配器接收和发送 Web Services 请求.业务流程可以发布为 Web Services 并使用外 ...

  7. 一封给JVM懵懂者的情书【不看错过一生幸福】

    别说你懂我 你只是在意Java你把我留在家里身和心却始终在她那里难道我只是她的附属品?错,我是我,我是JVM,没有我就没有他! 如果你想懂我或者不管你是否懂我我都在这里等你---[深入JVM内核—原理 ...

  8. IOS第18天(9,核心动画-动画组)

    ****动画组 // 核心动画都是假象,不能改变layer的真实属性的值// 展示的位置和实际的位置不同.实际位置永远在最开始位置 #import "HMViewController.h&q ...

  9. Python强化训练笔记(四)——字典的排序

    假如有学生成绩以字典顺序排列:{'Tom': 87, 'Jack': 90, 'Rose': 100.....} 想要根据学生的成绩来进行排序,可以考虑使用sorted函数.但是sorted函数用在字 ...

  10. 成功熬了四年还没死?一个IT屌丝创业者的深刻反思

    三个IT屌丝创业的故事 从前有三个屌丝,聚在一起做网络.提供免费的网络服务,砸锅卖铁,通宵达旦,除了卖肾,啥都做了.3年后终于做到了五百万用户.对于年轻人来说,能把五百万人玩弄于鼓掌之间,已经是很牛逼 ...