浅谈Rx响应式编程

目录
  • 一、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进行组合:

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响应式编程的资料请关注我们其它相关文章!

(0)

相关推荐

  • RxJava的消息发送和线程切换实现原理

    RxJava是一个在Java虚拟机上的响应式扩展,通过使用可观察的序列将异步和基于事件的程序组合起来的一个库. 它扩展了观察者模式来支持数据/事件序列,并且添加了操作符,这些操作符允许你声明性地组合序列,同时抽象出要关注的问题:比如低级线程.同步.线程安全和并发数据结构等. RxJava相信大家都非常了解吧,今天分享一下RxJava的消息发送和线程源码的分析.最后并分享一个相关demo,让大家更加熟悉我们天天都在用的框架. 消息订阅发送 首先让我们看看消息订阅发送最基本的代码组成: Observ

  • RxJS的入门指引和初步应用

    前言 RxJS是一个强大的Reactive编程库,提供了强大的数据流组合与控制能力,但是其学习门槛一直很高,本次分享期望从一些特别的角度解读它在业务中的使用,而不是从API角度去讲解. RxJS简介 通常,对RxJS的解释会是这么一些东西,我们来分别看看它们的含义是什么. Reactive Lodash for events Observable Stream-based 什么是Reactive呢,一个比较直观的对比是这样的: 比如说,abc三个变量之间存在加法关系: a = b + c 在传统

  • 谈谈RxJava2中的异常及处理方法

    前言 众所周知,RxJava2 中当链式调用中抛出异常时,如果没有对应的 Consumer 去处理异常,则这个异常会被抛出到虚拟机中去,Android 上的直接表现就是 crash,程序崩溃. 订阅方式 说异常处理前咱们先来看一下 RxJava2 中 Observable 订阅方法 subscribe() 我们常用的几种订阅方式: // 1 subscribe() // 2 Disposable subscribe(Consumer<? super T> onNext) // 3 Dispos

  • RxJava取消订阅的各种方式的实现

    手动取消订阅 Consumer类型 Observable创建返回Disposable取消 public class SecondActivity extends AppCompatActivity { private static final String TAG = "SecondActivity"; private Disposable disposable; @Override protected void onCreate(Bundle savedInstanceState)

  • 你用不惯 RxJava,只因缺了这把钥匙(推荐)

    前言 本文最初是为部门内部培训而准备的,但经过一番调研发现,同事们用不惯 RxJava,并不是因为网上介绍 "怎么用" 的教程不够多,恰恰是因为,一上来就急着发车的教程无数.却从未有过哪篇教程 舍得用几句话的功夫点破 RxJava 操作符究竟为何方神圣.我们为什么要用.为什么要那样用. Observable.just(1, 3, 5, 7, 9) .map(i -> i + 1) .filter(i -> i < 5) .subscribe(getObserve())

  • RxJava2和Retrofit2封装教程(整洁、简单、实用)

    前言 RxJava2与Retrofit2是老搭档了,之前写了一篇<RxJava和Retrofit2的统一处理单个请求>,是用的Rxjava1.0,本次使用Rxjava2.0与Retrofit2进行封装,一样整洁.简单.实用.Rxjava2相比Rxjava1优化和改动不少了东西,网上有很多大神写的文章,这里就不粘贴复制了.封装的过程有什么问题.疑问,请在下方留言. 下面话不多说了,来一起看看详细的介绍吧 封装教程如下: 核心网络请求: package com.lin.netrequestdemo

  • RxJava2 线程调度的方法

    subscribeOn和observeOn负责线程切换,同时某些操作符也默认指定了线程. 我们这里不分析在线程中怎么执行的.只看如何切换到某个指定线程. subscribeOn Observable.subscribeOn()在方法内部生成了一个ObservableSubscribeOn对象. 主要看一下ObservableSubscribeOn的subscribeActual方法. @Override public void subscribeActual(final Observer<? s

  • RxJS在TypeScript中的简单使用详解

    1. 安装 # 安装 typescript, rxjs 包 npm install -D typescript @types/node npm install rxjs 2. 使用 2.1 使用 from 来从数组生成源 RxJS 有许多创建源的方法,如 from, fromEvent..., 这里使用 from做个例子 import {from} from 'rxjs' // 从数组生成可订阅对象 // obser 的对象类型为 Observable let obser = from([1,2

  • 学习RxJS之JavaScript框架Cycle.js

    是什么 Cycle.js 是一个极简的JavaScript框架(核心部分加上注释125行),提供了一种函数式,响应式的人机交互接口(以下简称HCI): 函数式 Cycle.js 把应用程序抽象成一个纯函数 main(),从外部世界读取副作用(sources),然后产生输出(sinks) 传递到外部世界,在那形成副作用.这些外部世界的副作用,做为Cycle.js的插件存在(drivers),它们负责:处理DOM.提供HTTP访问等. 响应式 Cycle.js 使用 rx.js 来实现关注分离,这意

  • 浅谈Rx响应式编程

    目录 一.Observable 二.高阶函数 三.快递盒模型 3.1.快递盒模型1:fromEvent 3.2.快递盒模型2:interval 四.高阶快递盒 五.销毁快递盒 5.1.销毁快递盒--取消订阅 5.2.销毁高阶快递盒 六.补充 七.后记 一.Observable Observable从字面翻译来说叫做"可观察者",换言之就是某种"数据源"或者"事件源",这种数据源具有可被观察的能力,这个和你主动去捞数据有本质区别.用一个形象的比喻就

  • 浅谈Spring5 响应式编程

    近年来,响应式编程在开发者社区和客户中很受欢迎,由于其以声明的方式构建应用程序的能力,而不是强制,形成更加敏感和有弹性的应用.Spring 5 将反应系统纳入其核心框架的事实已经显示出向声明式编程的范式转变. 响应式编程管理数据生产者和消费者之间的异步数据流,它们需要以流畅的方式对数据进行响应.所以,响应式编程都是异步和事件驱动的流畅应用程序,需要少量的线程进行缩放. 响应式编程很难构建基于线程的架构,由于在基于共享可变状态.线程和锁的应用程序扩展过程中涉及到高度复杂性. 在响应式编程的上下文中

  • 浅谈Python响应式类库RxPy

    一.基本概念 Reactive X中有几个核心的概念,先来简单介绍一下. 1.1.Observable和Observer(可观察对象和观察者) 首先是Observable和Observer,它们分别是可观察对象和观察者.Observable可以理解为一个异步的数据源,会发送一系列的值.Observer则类似于消费者,需要先订阅Observable,然后才可以接收到其发射的值.可以说这组概念是设计模式中的观察者模式和生产者-消费者模式的综合体. 1.2.Operator(操作符) 另外一个非常重要

  • 浅谈Java响应式系统

    初识响应式系统 ReactiveX的本质就是Observer+Iterator+函数编程+异步.是一个事件驱动的,异步的,可观察的序列. 使用RxJava可以将异步的回调改写成为链式调用.在代码上看起来非常简洁明了.当然JDK也提供了CompletionStage提供了类似的解决回调的功能. Rxjava只是一个java的基本库,如果我们想要构建响应式的服务器,响应式的web,响应式的数据访问,甚至是响应式的微服务,又该如何处理呢? 这个时候我了解到了Vert.x.Vert.x就是用来构建Rea

  • 浅谈Vue响应式(数组变异方法)

    前言 很多初使用Vue的同学会发现,在改变数组的值的时候,值确实是改变了,但是视图却无动于衷,果然是因为数组太高冷了吗? 查看官方文档才发现,不是女神太高冷,而是你没用对方法. 看来想让女神自己动,关键得用对方法.虽然在官方文档中已经给出了方法,但是在下实在好奇的紧,想要解锁更多姿势的话,那就必须先要深入女神的心,于是乎才有了去探索Vue响应式原理的想法.(如果你愿意一层一层地剥开我的心.你会发现,你会讶异-- 沉迷于鬼哭狼嚎 无法自拔QAQ). 前排提示,Vue的响应式原理主要是使用了ES5的

  • 利用Swift实现一个响应式编程库

    前言 整个2017年我完全使用 Swift 进行开发了.使用 Swift 进行开发是一个很愉快的体验,我已经完全不想再去碰 OC 了.最近想做一个响应式编程的库,所以就把它拿来分享一下. 在缺乏好的资源的情况下,学习响应式编程成为痛苦.我开始学的时候,做死地找各种教程.结果发现有用的只是极少部分,而且这少部分也只是表面上的东西,对于整个体系结构的理解也起不了多大的作用. Reactive Programing 说到响应式编程,ReactiveCocoa 和 RxSwift 可以说是目前 iOS

  • 浅谈Java实现面向对象编程java oop

    一.对象的综述 面向对象编程(OOP)具有多方面的吸引力.对管理人员,它实现了更快和更廉价的开发与维护过程.对分析与设计人员,建模处理变得更加简单,能生成清晰.易于维护的设计方案.对程序员,对象模型显得如此高雅和浅显.此外,面向对象工具以及库的巨大威力使编程成为一项更使人愉悦的任务.每个人都可从中获益,至少表面如此. 所有编程语言的最终目的都是解决企业又或者人在现实生活中所遇到的问题,最初我们的程序可能长这样"11111100001",相信大家都不会陌生,只是大家没这么子去敲过代码.再

  • 浅谈Python的元编程

    目录 一.装饰器 二.装饰器的执行顺序 三.元类 四.descriptor 类(描述符类) 五.总结 相应的元编程就是描述代码本身的代码,元编程就是关于创建操作源代码(比如修改.生成或包装原来的代码)的函数和类.主要技术是使用装饰器.元类.描述符类. 一.装饰器 装饰器就是函数的函数,它接受一个函数作为参数并返回一个新的函数,在不改变原来函数代码的情况下为其增加新的功能,比如最常用的计时装饰器: from functools import wraps def timeit(logger=None

  • 关于springboot响应式编程整合webFlux的问题

    在servlet3.0标准之前,是每一个请求对应一个线程.如果此时一个线程出现了高延迟,就会产生阻塞问题,从而导致整个服务出现严重的性能情况,因为一旦要调用第三方接口,就有可能出现这样的操作了.早期的处理方式只能是手工控制线程. 在servlet3.0标准之后,为了解决此类问题,所以提供了异步响应的支持.在异步响应处理结构中,可以将耗时操作的部分交由一个专属的异步线程进行响应处理,同时请求的线程资源将被释放,并将该线程返回到线程池中,以供其他用户使用,这样的操作机制将极大的提升程序的并发性能.

  • springboot3+r2dbc响应式编程实践

    目录 r2dbc 工程依赖 配置文件 配置类 bean DAO controller Spring boot3已经M1了,最近群佬们也开始蠢蠢欲动的开始整活Reactive+Spring Boot3,跟着大家的步伐,我也来整一篇工程入门,我们将用java17+Spring Boot3+r2dbc+Reactive栈来讲述,欢迎大家来讨论.(关于响应式,请大家异步到之前的文章里,有详细介绍.) r2dbc Reactor还有基于其之上的Spring WebFlux框架.包括vert.x,rxjav

随机推荐