RxJS是一个JavaScript库,用来编写异步和基于事件的程序。RxJS结合了观察者模式、迭代器模式和使用集合的函数式编程,以知足以一种理想方式来管理事件序列所须要的一切。前端
能够把RxJS看成用来处理事件的 Lodash。
在如今的Web开发中,异步(Async)操做随处可见,好比使用ajax提交一个表单数据,咱们须要等待服务端返回提交结果后执行后续操做,这就是一个典型的异步操做。虽然JavaScript为了方便开发者进行异步操做,提出了不少解决方案(callback,Promise,Async/await等等),可是随着需求越发复杂,如何优雅的管理异步操做仍然是个难题。vue
此外,异步操做API千奇百怪,五花八门:ajax
以上这些经常使用的API所有都是异步的,可是每一个使用起来却彻底不一样,无形中给开发者增长了很大的学习和记忆成本。npm
使用RxJS能够很好的帮助咱们解决上面两个问题,控制大量异步代码的复杂度,保持代码可读性,并统一API。编程
举个栗子:页面上有一个搜索框,用户能够输入文本进行搜索,搜索时要向服务端发送异步请求,为了减少服务端压力,前端须要控制请求频率,1秒最多发送5次请求,而且输入为空时不发送请求,最后将搜索的结果显示在页面上。segmentfault
一般咱们的作法是这样的,先判断输入是否为空,若是不为空,则构造一个截流函数来控制请求频率,这其中涉及到建立和销毁定时器,此外,因为每一个请求返回时间不肯定,如何获取最后一次搜索结果,须要构造一个栈来保存请求顺序, 想完美实现需求并不简单。设计模式
RxJS是如何解决这个问题的呢?请看下面的代码:数组
// 1.获取dom元素 const typingInput = document.querySelector("#typing-input"); // 输入 const typingBack = document.querySelector("#typing-back"); // 输出 // 2.模拟异步请求 const getData = value => new Promise(resolve => setTimeout(() => resolve(`async data ${value}`), 1000) ); // 3.RxJS操做 const source$ = fromEvent(typingInput, "input") // 建立事件数据流 .pipe( // 管道式操做 map(e => e.target.value), // 获取输入的数据 filter(i => i), // 过滤空数据 debounceTime(200), // 控制频率 switchMap(getData) // 转化数据为请求 ); // 4.输入结果 source$.subscribe(asyncData => (typingBack.innerHTML = asyncData));
这就是所有代码,也许有些地方看不太懂 ,不要紧,先不要着急,咱们分步解读一下。网络
注意,这段代码咱们使用的所有变量都是用const声明的,所有是不可变的,也便是变量声明时是什么值,就永远是什么值,就像定义函数同样。相对于传统的指令式编程,RxJS的代码就是由一个一个不可变的函数组成,每一个函数只是对输入参数做出相应,而后返回结果,这样的代码写起来更加清爽,也更好维护。前端工程师
RxJS结合了函数式和响应式这两种编程思想,为了更深刻的了解RxJS,先来介绍一下什么是函数式编程和响应式编程。
函数式编程(Functional Porgramming)是一种编程范式,就像“面向对象编程”同样,是一种编写代码的“方法论”,告诉咱们应该如何思考和解决问题。不一样于面向对象编程,函数式编程强调使用函数来解决问题。
这里有两个问题:
与之对应的是命令式编程,也是最多见的编程模式。
举个例子,咱们但愿写个函数,把数组中的每一个元素乘以2,使用命令式编程,大概是这个样子的:
function double(arr) { const result = [] for(let i=0,l=arr.length;i<l;i++) { result.push(arr[i] * 2) } return result }
咱们将整个逻辑过程完整描述了一遍,完美。
但若是又来了一个新需求,实现一个新函数,把数组中每一个元素加1,简单,再来一遍:
function addOne(arr) { const result = [] for(let i=0,l=arr.length;i<l;i++) { result.push(arr[i] + 1) } return result }
是否是感受哪里不对?double和addOne百分之九十的代码彻底同样,“重复的代码是万恶之源。”咱们应该想办法改进一下。
这里就体现了命令式编程的一个问题,程序按照逻辑过程来执行,可是不少问题都有类似的模式,好比上面的double和addOne。很天然咱们想把这个模式抽象一下,减小重复代码。
接下来咱们使用JavaScript的map函数来重写double和addOne:
function double(arr) { return arr.map(function(item) { return item * 2 }) } function addOne(arr) { return arr.map(function(item) { return item + 1 }) }
重复代码所有被封装到map函数中。而咱们须要作的只是告诉map函数应该如何映射数据,这就是声明式编程。相比较以前的代码,这样的代码更容易维护。
若是使用箭头函数,代码还能够进一步简化:
const double = arr => arr.map(item => item * 2) const addOne = arr => arr.map(item => item + 1)
注意以上两个函数的返回结果都是一个新的数组,而并无对原数组进行修改,这符合函数式编程的另一个要求:纯函数。
纯函数是指知足如下两个条件的函数:
举个栗子:
const arr = [1, 2, 3, 4, 5] arr.slice(0, 3) // [1, 2, 3] arr.slice(0, 3) // [1, 2, 3] arr.slice(0, 3) // [1, 2, 3]
JavaScript中数组的slice方法无论执行几回,返回值都相同,而且没有改变任何外部状态,因此slice就是一个纯函数。
const arr = [1, 2, 3, 4, 5] arr.splice(0, 3) // [1, 2, 3] arr.splice(0, 3) // [4, 5] arr.slice(0, 3) // []
相反,splice方法每次调用的结果就不一样,由于splice方法改变了全局变量arr的值,因此splice就不是纯函数。
不纯的函数每每会产生一些反作用(Side Effect),好比如下这些:
使用纯函数能够大大加强代码的可维护性,由于固定输入老是返回固定输出,因此更容易写单元测试,也就更不容易产生bug。
数据不可变是函数式编程中十分重要的一个概念,意思是若是咱们想改变一个变量的值,不是直接对这个变量进行修改,而是经过调用函数,产生一个新的变量。
若是你是一个前端工程师,确定已经对数据不可变的好处深有体会。在JavaScript中,字符串(String),数字(Number)这两种类型就是不可变的,使用他们的时候每每不容易出错,而数组(Array)类型就是可变的, 使用数组的pop、push等方法都会改变原数组对象,从而引起各类bug。
注意,虽然ES6已经提出了使用const声明一个常量(不可变数据),可是这只能保证声明的对象的引用不可改变,而这个对象自身仍然能够变化。好比用const声明一个数组,使用push方法仍然能够像数组中添加元素。
和面向对象编程相比,面向对象编程更倾向把状态的改变封装到对象内部,以此让代码更清晰。而函数式编程倾向数据和函数分离,函数能够处理数据,但不改变原数据,而是经过产生新数据的方式做为运算结果,以此来尽可能减小变化的部分,让咱们的代码更清晰。
和函数式编程相似,响应式编程(Reactive Programming)也是一种编程的范式。从设计模式的角度来讲,响应式编程就是“观察者模式”的一种有效实践。简单来讲,响应式编程指当数据发生变化时,主动通知数据的使用者这个变化。
不少同窗都使用过vue框架开发,vue中很出名的数据双向绑定就是基于响应式编程的设计思想实现的。当咱们在经过v-bind绑定一个数据到组件上之后,无论这个数据什么时候发生变化,都会主动通知绑定过的组件,使咱们开发时能够专一处理数据自己,而不用关心如何同步数据。
而在相应时编程里最出名的框架就是微软开发的Reactive Extension。这套框架旨在帮助开发者解决复杂的异步处理问题。咱们的主角RxJS就是这个框架的JS版本。
安装
npm install rxjs
导入
import Rx from "rxjs";
请注意,这样导入会将整个RxJS库所有导入进来,而实际项目未必会用上Rxjs的所有功能,所有导入会让项目打包后变得很是大,咱们推荐使用深链(deep link)的方式导入Rxjs,只导入用的上的功能,好比咱们要使用Observable类,就只导入它:
import { Observable } from "rxjs/Observable";
实际项目中,按需导入是一个好办法,可是若是每一个文件都写一堆import语句,那就太麻烦了。因此,更好的实践是用一个文件专门导入RxJS相关功能,其余文件再导入这个文件,把RxJS导入工做集中管理。
篇幅有限,下一讲将会讲解RxJS中几个核心概念,欢迎各位留言拍砖~