Rx响应式编程如何深入浅出地理解?

更新于
2026-09-26 14:10:13
1阅读来源:SEO基础
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计2805个文字,预计阅读时间需要12分钟。

Rx响应式编程如何深入浅出地理解?

目录

一、Observable

二、高级函数

三、快速递盒模型

3.1 快速递盒模型:fromEvent

3.2 快速递盒模型:interval

四、高级快速递盒

五、销毁快速递盒

5.1 销毁快速递盒——取消订阅

5.2 销毁高级快速递盒

目录
  • 一、Observable
  • 二、高阶函数
  • 三、快递盒模型
    • 3.1、快递盒模型1:fromEvent
    • 3.2、快递盒模型2:interval
  • 四、高阶快递盒
    • 五、销毁快递盒
      • 5.1、销毁快递盒——取消订阅
      • 5.2、销毁高阶快递盒
    • 六、补充
      • 七、后记

        一、Observable

        Observable从字面翻译来说叫做“可观察者”,换言之就是某种“数据源”或者“事件源”,这种数据源具有可被观察的能力,这个和你主动去捞数据有本质区别。用一个形象的比喻就是Observable好比是水龙头,你可以去打开水龙头——订阅Observable,然后水——数据就会源源不断流出。这就是响应式编程的核心思想——变主动为被动。不过这个不在本篇文章中详解。

        Observable是一种概念,可以通过不同的方式去具体实现,本文通过高阶函数来实现两个常用Observable:fromEvent和Interval。通过讲解对Observable的订阅和取消订阅两个行为来帮助读者真正理解Observable是什么。

        二、高阶函数

        高阶函数的概念来源于函数式编程,简单的定义就是一个函数的入参或者返回值是一个函数的函数。例如:

        function foo(arg){ return function(){ console.log(arg) } } const bar = foo(“hello world”) bar() // hello world

        ps:高阶函数能做的事情很多,这里仅仅针对本文需要的情形进行使用。

        上面这个foo函数的调用并不会直接打印hello world,而只是把这个hello world给缓存起来。后面我们根据实际需要调用返回出来的bar函数,然后真正去执行打印hello world的工作。

        为啥要做这么一步封装呢?实际上这么做的效果就是“延迟”了调用。而一切的精髓就在这个“延迟”两个字里面。我们实际上是对一种行为进行了包装,看上去就像某种一致的东西,好比是快递盒子。

        里面可以装不同的东西,但对于物流来说就是统一的东西。因此,就可以形成对快递盒的统一操作,比如堆叠、运输、存储、甚至是打开盒子这个动作也是一致的。

        回到前面的例子,调用foo函数,相当于打包了一个快递盒,这个快递盒里面有一个固定的程序,就是当打开这个快递盒(调用bar)时执行一个打印操作。

        我们可以有foo1、foo2、foo3……里面有各种各样的程序,但是这些foos,都有一个共同的操作就是“打开”。(前提是这个foo会返回一个函数,这样才能满足“打开”的操作,即调用返回的函数)。

        function foo1(arg){ return function(){ console.log(arg+"?") } } function foo2(arg){ return function(){ console.log(arg+"!") } } const bar1 = foo1(“hello world”) const bar2 = foo2("yes") bar1()+bar2() // hello world? yes!

        三、快递盒模型

        3.1、快递盒模型1:fromEvent

        有了上面的基础,下面我们就来看一下Rx编程中最常用的一个Observable—fromEvent(……)。对于Rx编程的初学者,起初很难理解fromEvent(……)和addEventListener(……)有什么区别。

        btn.addEventListener("click",callback) rx.fromEvent(btn,"click").subscribe(callback)

        如果直接执行这个代码,确实效果是一样的。那么区别在哪儿呢?最直接的区别是,subscribe函数作用在fromEvent(……)上而不是btn上,而addEventListener是直接作用在btn上的。subscribe函数是某种“打开”操作,而fromEvent(……)则是某种快递盒。

        fromEvent实际上是对addEventListener的“延迟”调用

        function fromEvent(target,evtName){ return function(callback){ target.addEventListener(evtName,callback) } } const ob = fromEvent(btn,"click") ob(console.log)// 相当于 subscribe

        哦!fromEvent本质上是高阶函数

        至于如何实现subscribe来完成“打开”操作,不在本文讨论范围,在Rx编程中,这个subscribe的动作叫做“订阅”。“订阅”就是所有Observable的统一具备的操作。再次强调:本文中对Observable的“调用”在逻辑上相当于subscribe。

        下面再举一个例子,基本可以让读者举二反N了。

        3.2、快递盒模型2:interval

        Rx中有一个interval,它和setInterval有什么区别呢?

        估计有人已经开始抢答了,interval就是对setInterval的延迟调用!bingo!

        function interval(period){ let i = 0 return function(callback){ setInterval(period,()=>callback(i++)) } } const ob = interval(1000) ob(console.log)// 相当于 subscribe

        从上面两个例子来看,无论是fromEvent(……)还是Interval(……),虽然内部是完全不同的逻辑,但是他们同属于“快递盒”这种东西,我们把它称之为Observable——可观察者。

        fromEvent和Interval本身只是制作“快递盒”的模型,只有调用后返回的东西才是“快递盒”,即fromEvent(btn,"click")、interval(1000) 等等...

        四、高阶快递盒

        有了上面的基础,下面开始进阶:我们拥有了那么多快递盒,那么就可以对这些快递盒再封装。

        在文章开头说了,快递盒统一了一些操作,所以我们可以把许许多多的快递盒堆叠在一起,即组合成一个大的快递盒!这个大的快递盒和小的快递盒一样,具有“打开”操作(即订阅)。当我们打开这个大的快递盒的时候,会发生什么呢?

        可以有很多种不同的可能性,比如可以逐个打开小的快递盒(concat),或者一次性打开所有小的快递盒(merge),也可以只打开那个最容易打开的快递盒(race)。

        下面是一个简化版的merge方法:

        function merge(...obs){ return function(callback){ obs.forEach(ob=>ob(callback)) // 打开所有快递盒 } }

        我们还是拿之前的fromEvent和interval来举例吧!

        使用merge方法对两个Observable进行组合:

        Rx响应式编程如何深入浅出地理解?

        const ob1 = fromEvent(btn,'click') // 制作快递盒1 const ob2 = interval(1000) // 制作快递盒2 const ob = merge(ob1,ob2) //制作大快递盒 ob(console.log) // 打开大快递盒

        当我们“打开”(订阅)这个大快递盒ob的时候,其中两个小快递盒也会被“打开”(订阅),任意一个小快递盒里面的逻辑都会被执行,我们就合并(merge)了两个Observable,变成了一个。

        这就是我们为什么要辛辛苦苦把各种异步函数封装成快递盒(Observable)的原因了——方便对他们进行统一操作!当然仅仅只是“打开”(订阅)这个操作只是最初级的功能,下面开始进阶。

        五、销毁快递盒

        5.1、销毁快递盒——取消订阅

        我们还是以fromEvent为例子,之前我们写了一个简单的高阶函数,作为对addEventListener的封装:

        function fromEvent(target,evtName){ return function(callback){ target.addEventListener(evtName,callback) } }

        当我们调用这个函数的时候,就生成了一个快递盒(fromEvent(btn,'click'))。当我们调用了这个函数返回的函数的时候,就是打开了快递盒(fromEvent(btn,'click')(console.log))。

        那么我们怎么去销毁这个打开的快递盒呢?

        首先我们需要得到一个已经打开的快递盒,上面的函数调用结果是void,我们无法做任何操作,所以我们需要构造出一个打开状态的快递盒。还是使用高阶函数的思想:在返回的函数里面再返回一个函数,用于销毁操作。

        function fromEvent(target,evtName){ return function(callback){ target.addEventListener(evtName,callback) return function(){ target.removeEventListener(evtName,callback) } } } const ob = fromEvent(btn,'click') // 制作快递盒 const sub = ob(console.log) // 打开快递盒,并得到一个可用于销毁的函数 sub() // 销毁快递盒

        同理,对于interval,我们也可以如法炮制:

        function interval(period){ let i = 0 return function(callback){ let id = setInterval(period,()=>callback(i++)) return function(){ clearInterval(id) } } } const ob = interval(1000) // 制作快递盒 const sub = ob(console.log) // 打开快递盒 sub() // 销毁快递盒

        5.2、销毁高阶快递盒

        我们以merge为例:

        function merge(...obs){ return function(callback){ const subs = obs.map(ob=>ob(callback)) // 订阅所有并收集所有的销毁函数 return function(){ subs.forEach(sub=>sub()) // 遍历销毁函数并执行 } } } const ob1 = fromEvent(btn,'click') // 制作快递盒1 const ob2 = interval(1000) // 制作快递盒2 const ob = merge(ob1,ob2) //制作大快递盒 const sub = ob(console.log) // 打开大快递盒 sub() // 销毁大快递盒

        当我们销毁大快递盒的时候,就会把里面所有的小快递盒一起销毁。

        六、补充

        到这里我们已经将Observable的两个重要操作(订阅、取消订阅)讲完了,值得注意的是,取消订阅这个行为并非是作用于Observable上,而是作用于已经“打开”的快递盒(订阅Observable后返回的东西)之上!

        Observable除此以外,还有两个重要操作,即发出事件、完成/异常,(这两个操作属于是由Observable主动发起的回调,和操作的方向是相反的,所以其实不能称之为操作)。

        这个两个行为用快递盒就不那么形象了,我们可以将Observable比做是水龙头,原先的打开快递盒变成拧开水龙头,而我们传入的回调函数就可以比喻成接水的水杯!由于大家对回调函数已经非常熟悉了,所以本文就不再赘述了。

        七、后记

        总结一下我们学习的内容,我们通过高阶函数将一些操作进行了“延迟”,并赋予了统一的行为,比如“订阅”就是延迟执行了异步函数,“取消订阅”就是在上面的基础上再“延迟”执行了销毁资源的函数。

        这些所谓的“延迟”执行就是Rx编程中幕后最难理解,也是最核心的部分。Rx的本质就是将异步函数封装起来,然后抽象成四大行为:订阅、取消订阅、发出事件、完成/异常。

        实际实现Rx库的方法有很多,本文只是利用了高阶函数的思想来帮助大家理解Observable的本质,在官方实现的版本中,Observable这个快递盒并非是高阶函数,而是一个对象,但本质上是一样的

        以上就是浅谈Rx响应式编程的详细内容,更多关于Rx响应式编程的资料请关注自由互联其它相关文章!

        本文共计2805个文字,预计阅读时间需要12分钟。

        Rx响应式编程如何深入浅出地理解?

        目录

        一、Observable

        二、高级函数

        三、快速递盒模型

        3.1 快速递盒模型:fromEvent

        3.2 快速递盒模型:interval

        四、高级快速递盒

        五、销毁快速递盒

        5.1 销毁快速递盒——取消订阅

        5.2 销毁高级快速递盒

        目录
        • 一、Observable
        • 二、高阶函数
        • 三、快递盒模型
          • 3.1、快递盒模型1:fromEvent
          • 3.2、快递盒模型2:interval
        • 四、高阶快递盒
          • 五、销毁快递盒
            • 5.1、销毁快递盒——取消订阅
            • 5.2、销毁高阶快递盒
          • 六、补充
            • 七、后记

              一、Observable

              Observable从字面翻译来说叫做“可观察者”,换言之就是某种“数据源”或者“事件源”,这种数据源具有可被观察的能力,这个和你主动去捞数据有本质区别。用一个形象的比喻就是Observable好比是水龙头,你可以去打开水龙头——订阅Observable,然后水——数据就会源源不断流出。这就是响应式编程的核心思想——变主动为被动。不过这个不在本篇文章中详解。

              Observable是一种概念,可以通过不同的方式去具体实现,本文通过高阶函数来实现两个常用Observable:fromEvent和Interval。通过讲解对Observable的订阅和取消订阅两个行为来帮助读者真正理解Observable是什么。

              二、高阶函数

              高阶函数的概念来源于函数式编程,简单的定义就是一个函数的入参或者返回值是一个函数的函数。例如:

              function foo(arg){ return function(){ console.log(arg) } } const bar = foo(“hello world”) bar() // hello world

              ps:高阶函数能做的事情很多,这里仅仅针对本文需要的情形进行使用。

              上面这个foo函数的调用并不会直接打印hello world,而只是把这个hello world给缓存起来。后面我们根据实际需要调用返回出来的bar函数,然后真正去执行打印hello world的工作。

              为啥要做这么一步封装呢?实际上这么做的效果就是“延迟”了调用。而一切的精髓就在这个“延迟”两个字里面。我们实际上是对一种行为进行了包装,看上去就像某种一致的东西,好比是快递盒子。

              里面可以装不同的东西,但对于物流来说就是统一的东西。因此,就可以形成对快递盒的统一操作,比如堆叠、运输、存储、甚至是打开盒子这个动作也是一致的。

              回到前面的例子,调用foo函数,相当于打包了一个快递盒,这个快递盒里面有一个固定的程序,就是当打开这个快递盒(调用bar)时执行一个打印操作。

              我们可以有foo1、foo2、foo3……里面有各种各样的程序,但是这些foos,都有一个共同的操作就是“打开”。(前提是这个foo会返回一个函数,这样才能满足“打开”的操作,即调用返回的函数)。

              function foo1(arg){ return function(){ console.log(arg+"?") } } function foo2(arg){ return function(){ console.log(arg+"!") } } const bar1 = foo1(“hello world”) const bar2 = foo2("yes") bar1()+bar2() // hello world? yes!

              三、快递盒模型

              3.1、快递盒模型1:fromEvent

              有了上面的基础,下面我们就来看一下Rx编程中最常用的一个Observable—fromEvent(……)。对于Rx编程的初学者,起初很难理解fromEvent(……)和addEventListener(……)有什么区别。

              btn.addEventListener("click",callback) rx.fromEvent(btn,"click").subscribe(callback)

              如果直接执行这个代码,确实效果是一样的。那么区别在哪儿呢?最直接的区别是,subscribe函数作用在fromEvent(……)上而不是btn上,而addEventListener是直接作用在btn上的。subscribe函数是某种“打开”操作,而fromEvent(……)则是某种快递盒。

              fromEvent实际上是对addEventListener的“延迟”调用

              function fromEvent(target,evtName){ return function(callback){ target.addEventListener(evtName,callback) } } const ob = fromEvent(btn,"click") ob(console.log)// 相当于 subscribe

              哦!fromEvent本质上是高阶函数

              至于如何实现subscribe来完成“打开”操作,不在本文讨论范围,在Rx编程中,这个subscribe的动作叫做“订阅”。“订阅”就是所有Observable的统一具备的操作。再次强调:本文中对Observable的“调用”在逻辑上相当于subscribe。

              下面再举一个例子,基本可以让读者举二反N了。

              3.2、快递盒模型2:interval

              Rx中有一个interval,它和setInterval有什么区别呢?

              估计有人已经开始抢答了,interval就是对setInterval的延迟调用!bingo!

              function interval(period){ let i = 0 return function(callback){ setInterval(period,()=>callback(i++)) } } const ob = interval(1000) ob(console.log)// 相当于 subscribe

              从上面两个例子来看,无论是fromEvent(……)还是Interval(……),虽然内部是完全不同的逻辑,但是他们同属于“快递盒”这种东西,我们把它称之为Observable——可观察者。

              fromEvent和Interval本身只是制作“快递盒”的模型,只有调用后返回的东西才是“快递盒”,即fromEvent(btn,"click")、interval(1000) 等等...

              四、高阶快递盒

              有了上面的基础,下面开始进阶:我们拥有了那么多快递盒,那么就可以对这些快递盒再封装。

              在文章开头说了,快递盒统一了一些操作,所以我们可以把许许多多的快递盒堆叠在一起,即组合成一个大的快递盒!这个大的快递盒和小的快递盒一样,具有“打开”操作(即订阅)。当我们打开这个大的快递盒的时候,会发生什么呢?

              可以有很多种不同的可能性,比如可以逐个打开小的快递盒(concat),或者一次性打开所有小的快递盒(merge),也可以只打开那个最容易打开的快递盒(race)。

              下面是一个简化版的merge方法:

              function merge(...obs){ return function(callback){ obs.forEach(ob=>ob(callback)) // 打开所有快递盒 } }

              我们还是拿之前的fromEvent和interval来举例吧!

              使用merge方法对两个Observable进行组合:

              Rx响应式编程如何深入浅出地理解?

              const ob1 = fromEvent(btn,'click') // 制作快递盒1 const ob2 = interval(1000) // 制作快递盒2 const ob = merge(ob1,ob2) //制作大快递盒 ob(console.log) // 打开大快递盒

              当我们“打开”(订阅)这个大快递盒ob的时候,其中两个小快递盒也会被“打开”(订阅),任意一个小快递盒里面的逻辑都会被执行,我们就合并(merge)了两个Observable,变成了一个。

              这就是我们为什么要辛辛苦苦把各种异步函数封装成快递盒(Observable)的原因了——方便对他们进行统一操作!当然仅仅只是“打开”(订阅)这个操作只是最初级的功能,下面开始进阶。

              五、销毁快递盒

              5.1、销毁快递盒——取消订阅

              我们还是以fromEvent为例子,之前我们写了一个简单的高阶函数,作为对addEventListener的封装:

              function fromEvent(target,evtName){ return function(callback){ target.addEventListener(evtName,callback) } }

              当我们调用这个函数的时候,就生成了一个快递盒(fromEvent(btn,'click'))。当我们调用了这个函数返回的函数的时候,就是打开了快递盒(fromEvent(btn,'click')(console.log))。

              那么我们怎么去销毁这个打开的快递盒呢?

              首先我们需要得到一个已经打开的快递盒,上面的函数调用结果是void,我们无法做任何操作,所以我们需要构造出一个打开状态的快递盒。还是使用高阶函数的思想:在返回的函数里面再返回一个函数,用于销毁操作。

              function fromEvent(target,evtName){ return function(callback){ target.addEventListener(evtName,callback) return function(){ target.removeEventListener(evtName,callback) } } } const ob = fromEvent(btn,'click') // 制作快递盒 const sub = ob(console.log) // 打开快递盒,并得到一个可用于销毁的函数 sub() // 销毁快递盒

              同理,对于interval,我们也可以如法炮制:

              function interval(period){ let i = 0 return function(callback){ let id = setInterval(period,()=>callback(i++)) return function(){ clearInterval(id) } } } const ob = interval(1000) // 制作快递盒 const sub = ob(console.log) // 打开快递盒 sub() // 销毁快递盒

              5.2、销毁高阶快递盒

              我们以merge为例:

              function merge(...obs){ return function(callback){ const subs = obs.map(ob=>ob(callback)) // 订阅所有并收集所有的销毁函数 return function(){ subs.forEach(sub=>sub()) // 遍历销毁函数并执行 } } } const ob1 = fromEvent(btn,'click') // 制作快递盒1 const ob2 = interval(1000) // 制作快递盒2 const ob = merge(ob1,ob2) //制作大快递盒 const sub = ob(console.log) // 打开大快递盒 sub() // 销毁大快递盒

              当我们销毁大快递盒的时候,就会把里面所有的小快递盒一起销毁。

              六、补充

              到这里我们已经将Observable的两个重要操作(订阅、取消订阅)讲完了,值得注意的是,取消订阅这个行为并非是作用于Observable上,而是作用于已经“打开”的快递盒(订阅Observable后返回的东西)之上!

              Observable除此以外,还有两个重要操作,即发出事件、完成/异常,(这两个操作属于是由Observable主动发起的回调,和操作的方向是相反的,所以其实不能称之为操作)。

              这个两个行为用快递盒就不那么形象了,我们可以将Observable比做是水龙头,原先的打开快递盒变成拧开水龙头,而我们传入的回调函数就可以比喻成接水的水杯!由于大家对回调函数已经非常熟悉了,所以本文就不再赘述了。

              七、后记

              总结一下我们学习的内容,我们通过高阶函数将一些操作进行了“延迟”,并赋予了统一的行为,比如“订阅”就是延迟执行了异步函数,“取消订阅”就是在上面的基础上再“延迟”执行了销毁资源的函数。

              这些所谓的“延迟”执行就是Rx编程中幕后最难理解,也是最核心的部分。Rx的本质就是将异步函数封装起来,然后抽象成四大行为:订阅、取消订阅、发出事件、完成/异常。

              实际实现Rx库的方法有很多,本文只是利用了高阶函数的思想来帮助大家理解Observable的本质,在官方实现的版本中,Observable这个快递盒并非是高阶函数,而是一个对象,但本质上是一样的

              以上就是浅谈Rx响应式编程的详细内容,更多关于Rx响应式编程的资料请关注自由互联其它相关文章!