关于译者:这是一个流淌着沪江血液的纯粹工程:认真,是 HTML 最坚实的梁柱;分享,是 CSS 里最闪耀的一瞥;总结,是 JavaScript 中最严谨的逻辑。经过捶打磨练,成就了本书的中文版。本书包含了函数式编程之精髓,希望可以帮助大家在学习函数式编程的道路上走的更顺畅。比心。

译者团队(排名不分先后):阿希bluekenbrucechamcfanlifedailkyoko-dfl3velilinsLittlePineappleMatildaJin冬青pobusamaCherry萝卜vavd317vivaxy萌萌zhouyao

第 10 章:异步的函数式(上)

阅读到这里,你已经学习了我所说的所有轻量级函数式编程的基础概念,在本章节中,我们将把这些概念应有到不同的情景当中,但绝对不会有新的知识点。

到目前为止,我们所说的一切都是同步的,意味着我们调用函数,传入参数后马上就会得到返回值。大部分的情况下是没问题的,但这几乎满足不了现有的 JS 应用。为了能在当前的 JS 环境里使用上函数式编程,我们需要去了解异步的函数式编程。

本章的目的是拓展我们对用函数式编程管理数据的思维,以便之后我们在更多的业务上应用。

时间状态

在你所有的应用里,最复杂的状态就是时间。当你操作的数据状态改变过程比较直观的时候,是很容易管理的。但是,如果状态随着时间因为响应事件而隐晦的变化,管理这些状态的难度将会成几何级增长。

我们在本文中介绍的函数式编程可以让代码变得更可读,从而增强了可靠性和可预见性。但是当你添加异步操作到你的项目里的时候,这些优势将会大打折扣。

必须明确的一点是:并不是说一些操作不能用同步来完成,或者触发异步行为很容易。协调那些可能会改变应用程序的状态的响应,这需要大量额外的工作。

所以,作为作者的你最好付出一些努力,或者只是留给阅读你代码的人一个难题,去弄清楚如果 A 在 B 之前完成,项目中状态是什么,还有相反的情况是什么?这是一个浮夸的问题,但以我的观点来看,这有一个确切的答案:如果可以把复杂的代码变得更容易理解,作者就必须花费更多心思。

减少时间状态

异步编程最为重要的一点是通过抽象时间来简化状态变化的管理。

为说明这一点,让我们先来看下一种有竞争状态(又称,时间复杂度)的糟糕情况,且必须手动去管理里面的状态:

var customerId = 42;
var customer; lookupCustomer( customerId, function onCustomer(customerRecord){
var orders = customer ? customer.orders : null;
customer = customerRecord;
if (orders) {
customer.orders = orders;
}
} ); lookupOrders( customerId, function onOrders(customerOrders){
if (!customer) {
customer = {};
}
customer.orders = customerOrders;
} );

回调函数 onCustomer(..)onOrders(..) 之间是互为竞争关系。假设他们都在运行,两者都有可能先运行,那将无法预测到会发生什么。

如果我们可以把 lookupOrders(..) 写到 onCustomer(..) 里面,那我们就可以确认 onOrders(..) 会在 onCustomer(..) 之后运行,但我们不能这么做,因为我们需要让 2 个查询同时执行。

所以,为了让这个基于时间的复杂状态正常化,我们用相应的 if-声明在各自的回调函数里来检查外部作用域的变量 customer。当各自的回调函数被执行,将会去检测 customer 的状态,从而确定各自的执行顺序,如果 customer 在回调函数里还没被定义,那他就是先运行的,否则则是第二个运行的。

这些代码可以运行,但是他违背了可读性的原则。时间复杂度让这个代码变得难以阅读。

让我们改用 JS promise 来把时间因素抽离出来:

var customerId = 42;

var customerPromise = lookupCustomer( customerId );
var ordersPromise = lookupOrders( customerId ); customerPromise.then( function onCustomer(customer){
ordersPromise.then( function onOrders(orders){
customer.orders = orders;
} );
} );

现在 onOrders(..) 回调函数存在 onCustomer(..) 回调函数里,所以他们各自的执行顺序是可以保证的。在各自的 then(..) 运行之前 lookupCustomer(..)lookupOrders(..) 被分别的调用,两个查询就已经并行的执行完了。

这可能不太明显,但是这个代码里还有其他内在的竞争状态,那就是 promise 的定义没有被体现出来。如果 orders 的查询在把 onOrders(..) 回调函数被 ordersPromise.then(..) 调用前完成,那么就需要一些比较智能的 东西 来保存 orders 直到 onOrders(..) 能被调用。 同理,record (或者说customer)对象是否能在 onCustomer(..) 执行时被接收到。

这里的 东西 和我们之前讨论过的时间复杂度类似。但我们不必去担心这些复杂性,无论是编码或者是读(更为重要)这些代码的时候,因为对我们来说,promise 所处理的就是时间复杂度上的问题。

promise 以时间无关的方式来作为一个单一的值。此外,获取 promise 的返回值是异步的,但却是通过同步的方法来赋值。或者说, promise 给 = 操作符扩展随时间动态赋值的功能,通过可靠的(时间无关)方式。

接下来我们将探索如何以相同的方式,在时间上异步地拓展本书之前同步的函数式编程操作。

积极的 vs 惰性的

积极的和惰性的在计算机科学的领域并不是表扬或者批评的意思,而是描述一个操作是否立即执行或者是延时执行。

我们在本例子中看到的函数式编程操作可以被称为积极的,因为它们同步(即时)地操作着离散的即时值或值的列表/结构上的值。

回忆下:

var a = [1,2,3]

var b = a.map( v => v * 2 );

b;			// [2,4,6]

这里 ab 的映射就是积极的,因为它在执行的那一刻映射了数组 a 里的所有的值,然后生成了一个新的数组 b 。即使之后你去修改 a ,比如说添加一个新的值到数组的最后一位,也不会影响到 b 的内容。这就是积极的函数式编程。

但是如果是一个惰性的函数式编程操作呢?思考如下情况:

var a = [];

var b = mapLazy( a, v => v * 2 );

a.push( 1 );

a[0];		// 1
b[0]; // 2 a.push( 2 ); a[1]; // 2
b[1]; // 4

我们可以想象下 mapLazy(..) 本质上 “监听” 了数组 a,只要一个新的值添加到数组的末端(使用 push(..)),它都会运行映射函数 v => v * 2 并把改变后的值添加到数组 b 里。

注意: mapLazy(..) 的实现没有被写出来,是因为它是虚构的方法,是不存在的。如果要实现 ab 之间的惰性的操作,那么简单的数组就需要变得更加聪明。

考虑下把 ab 关联到一起的好处,无论何时何地,你添加一个值进 a 里,它都将改变且映射到 b 里。它比同为声明式函数式编程的 map(..) 更强大,但现在它可以随时地变化,进行映射时你不用知道 a 里面所有的值。

** 【上一章】翻译连载 | 第 9 章:递归(下)-《JavaScript轻量级函数式编程》 |《你不知道的JS》姊妹篇 **

iKcamp原创新书《移动Web前端高效开发实战》已在亚马逊、京东、当当开售。

iKcamp官网:http://www.ikcamp.com

翻译连载 | 第 10 章:异步的函数式(上)-《JavaScript轻量级函数式编程》 |《你不知道的JS》姊妹篇的更多相关文章

  1. 翻译连载 | 第 10 章:异步的函数式(下)-《JavaScript轻量级函数式编程》 |《你不知道的JS》姊妹篇

    原文地址:Functional-Light-JS 原文作者:Kyle Simpson-<You-Dont-Know-JS>作者 关于译者:这是一个流淌着沪江血液的纯粹工程:认真,是 HTM ...

  2. 翻译连载 | 第 11 章:融会贯通 -《JavaScript轻量级函数式编程》 |《你不知道的JS》姊妹篇

    原文地址:Functional-Light-JS 原文作者:Kyle Simpson-<You-Dont-Know-JS>作者 关于译者:这是一个流淌着沪江血液的纯粹工程:认真,是 HTM ...

  3. 翻译连载 | 第 9 章:递归(下)-《JavaScript轻量级函数式编程》 |《你不知道的JS》姊妹篇

    原文地址:Functional-Light-JS 原文作者:Kyle Simpson-<You-Dont-Know-JS>作者 关于译者:这是一个流淌着沪江血液的纯粹工程:认真,是 HTM ...

  4. 翻译连载 | 附录 A:Transducing(上)-《JavaScript轻量级函数式编程》 |《你不知道的JS》姊妹篇

    原文地址:Functional-Light-JS 原文作者:Kyle Simpson-<You-Dont-Know-JS>作者 关于译者:这是一个流淌着沪江血液的纯粹工程:认真,是 HTM ...

  5. 翻译连载 |《你不知道的JS》姊妹篇 |《JavaScript 轻量级函数式编程》- 引言&前言

    原文地址:Functional-Light-JS 原文作者:Kyle Simpson-<You-Dont-Know-JS>作者 译者团队(排名不分先后):阿希.blueken.brucec ...

  6. 翻译连载 | 附录 A:Transducing(下)-《JavaScript轻量级函数式编程》 |《你不知道的JS》姊妹篇

    原文地址:Functional-Light-JS 原文作者:Kyle Simpson-<You-Dont-Know-JS>作者 关于译者:这是一个流淌着沪江血液的纯粹工程:认真,是 HTM ...

  7. 翻译连载 | 附录 B: 谦虚的 Monad-《JavaScript轻量级函数式编程》 |《你不知道的JS》姊妹篇

    原文地址:Functional-Light-JS 原文作者:Kyle Simpson-<You-Dont-Know-JS>作者 关于译者:这是一个流淌着沪江血液的纯粹工程:认真,是 HTM ...

  8. 翻译连载 | 附录 C:函数式编程函数库-《JavaScript轻量级函数式编程》 |《你不知道的JS》姊妹篇

    原文地址:Functional-Light-JS 原文作者:Kyle Simpson-<You-Dont-Know-JS>作者 关于译者:这是一个流淌着沪江血液的纯粹工程:认真,是 HTM ...

  9. 全本 | iKcamp翻译 | 《JavaScript 轻量级函数式编程》|《你不知道的JS》姊妹篇

    原文地址:Functional-Light-JS 原文作者:Kyle Simpson - <You-Dont-Know-JS>作者 译者团队(排名不分先后):阿希.blueken.bruc ...

随机推荐

  1. [2016-07-15]nuget包管理器控制台下的powershell脚本介绍

    博客有阵子没打理了,今天刚恢复样式,但是标题还是不太正常,总算能凑合看看. 回到正题,最近为了能在VS的程序包管理器控制台上能方便的自定义ps脚本去调整project的package,就开始看powe ...

  2. LVS-DR实现web调度模式

    author:JevonWei 版权声明:原创作品 实现LVS-DR调度web模式 拓扑环境 网络环境 RS1 RIP 192.168.198.138/24 VIP 192.168.198.100/3 ...

  3. WebService两种调用方法

    1.wsimport生成本地客户端代码 命令提示窗口执行生成命令. 格式:wsimport -s "src目录" -p “生成类所在包名” -keep “wsdl发布地址” 示例: ...

  4. hdu 6199 沈阳网络赛---gems gems gems(DP)

    题目链接 Problem Description Now there are n gems, each of which has its own value. Alice and Bob play a ...

  5. 利用 VMWare 搭建随机拓扑网络

    这篇文章是计算机网络上机实验课的作业. 实验任务:利用 VMWare 搭建一个由 5 个主机组成的随机拓扑的网络.要求该网络中至少有 2 个子网,两个路由器 .实验的网络拓扑图如下: 网络中有两个路由 ...

  6. wineshark分析抓取本地回环包

    wineshark分析抓取本地回环包 摘要 由于windows系统没有提供本地回环网络的接口,用Wireshark监控网络的话看不到localhost的流量.想要获取本地的网络数据包,可以通过一款小巧 ...

  7. Brotli、Deflate、Zopfli、LZMA、LZHAM、Bzip2六种无损数据压缩性能比较

    这里比较了六种数据压缩算法,结果表明Brotli算法会代替普遍使用的Deflate算法.分别用Canterbury compression corpus,web contentcorpus,和 enw ...

  8. KVM虚拟化主机安装

    KVM虚拟化主机安装 最小化安装CentOS6.X或者CentOS7.X,RHEL6.X以上系列建议建议选择安装最小虚拟化主机 如果要安装桌面可以先选择最小化虚拟主机,再选择Gnome桌面包 安装过程 ...

  9. 201521123030《Java程序设计》第4周学习总结

    1. 本周学习总结 1.1 尝试使用思维导图总结有关继承的知识点. 1.2 使用常规方法总结其他上课内容. 控制可见性的4个访问修饰符 private -- 仅对本类可见 public -- 对所有类 ...

  10. 201521123077 《Java程序设计》第1周学习总结

    1. 本章学习总结 java语言的历史 dos下运行 java文件 Math ,String ,Scanner,Interger等常用类的使用 jdk,jre,jvm等概念的了解 classpath, ...