RXJS概念的个人理解:响应式编程思想, 及其参照表(非常有用! )

57 篇文章 3 订阅
13 篇文章 0 订阅

Observables 是多个值的惰性推送集合。它填补了下面表格中的空白

 首先理解 pull 和 push。

pull直接理解为 使用者主动拿来用。

push理解为 数据发送者推送出去,使用者等待数据流动到,并且接受。

那么observable就是多个值的推送合集(异步序列sequence),或者直白粗暴理解,多个promise的集合。

前置知识:响应式编程 Reactive Programming

Event Buses、点击事件,http请求都是异步流。开发者可以观测这些异步流,并调用特定的逻辑对它们进行处理。使用Reactive如同开挂:你可以创建点击、悬停之类的任意流。通常流廉价(点击一下就出来一个)而无处不在,种类丰富多样:变量,用户输入,属性,缓存,数据结构等等都可以产生流。举例来说:微博回文(译者注:比如你关注的微博更新了)和点击事件都是流:你可以监听流并调用特定的逻辑对它们进行处理。

基于流的概念,RP赋予了你一系列神奇的函数工具集,使用他们可以合并、创建、过滤这些流。 一个流或者一系列流可以作为另一个流的输入。你可以 合并 多个流(经常使用的forkjoin操作符,相当于promise中的promiseall),从一堆流中 过滤 你真正感兴趣的那一些,将值从一个流 映射 到另一个流。

流(stream)是包含了 有时序(sequence),正在进行事件 的序列,可以发射(emmit)值(某种类型)、错误、完成信号。流在包含按钮的浏览器窗口被关闭时发出完成信号。

我们 异步地 捕获发射的事件,定义一系列函数在值被发射后,在错误被发射后,或在完成信号被发射后执行。有时,我们忽略对错误、完成信号地处理,仅仅关注对值的处理。对流进行监听,通常称为 订阅 ,处理流的函数是观测者observer,流是被观测的主体subject。这就是观察者设计模式(这句话是重点概念)

--a---b-c---d---X---|->

a, b, c, d 是被发射的值
X 是一个错误
| 是 ‘完成’ 信号
---> 是时间线

首先我们创建一个计数流来表明按钮被点击的次数。在RP中,每一个流都拥有一系列方法,例如mapfilterscan 等等。当你在流上调用这些方法,例如clickStream.map(f),会返回基于点击事件流的 新的流 ,同时原来的点击事件流并不会被改变,这个特性被称为 不可变性(immutability) 。不可变性与RP配合相得益彰。我们可以链式地调用他们:clickStream.map(f).scan(g):

  clickStream: ---c----c--c----c------c-->
               vvvvv map(c becomes 1) vvvv
               ---1----1--1----1------1-->
               vvvvvvvvv scan(+) vvvvvvvvv
counterStream: ---1----2--3----4------5-->

map(f) 函数对原来的流使用我们提供的f函数进行转换,并生成新的流。在上面的例子中,我们将每一次点击映射为数字1。scan(g)函数将所有流产生的值进行汇总,通过传入x = g(accumulated, current)函数产生新的值,g 是简单的求和函数。最后 counterStream在点击发生后发射点击事件发生的总数。

那么Promise也是可观察对象吗?

是的!

可观察对象(Observable)是Promise++(原文Promise++,可以对比C,C++,C++在兼容C的同时引入了面向对象等特性)。 在Rx环境中,你可以简单的通过var stream = Rx.Observable.fromPromise(promise)将Promise转换为可观察对象, 我们后面将这样使用, 唯一的区别是,可观察对象与Promises/A+ 并不兼容, 但是理论上不会产生冲突。 Promise 可以看做只能发射单值的可观察对象,Rx流则允许返回多个值。

两个场景来理解这两者的区别:

1. promise字面意思,承诺。 ‘我写完这段代码就去干一些别的事情’  此时, ‘别的事情’ 可以是 ‘摸鱼’ 或者 ‘喝咖啡’ 获取其他事情

2.Rxjs是一段流(stream)。举个例子:我作为一个observer,我要去订阅subject报纸。 报社发报纸的过程是一个异步流,月底报纸印刷完成了是一个observable object.  那么此时报社知道我订阅过报纸,报纸就会被送到我家门口。observableObject.subject( observer )

关于使用RXJS的时机

1. 当你处理多个异步函数序列sequence。(这种情况下需要建立一个状态机函数来分别处理不同的异步结果。此时可以使用RXJS的API,主要是对于多个异步流的处理

2. 如果所涉及的应用程序/类库具有非常少的基于异步/基于事件的操作, 那就不要用rxjs,只用promise

var input = document.getElementById('input');
var dictionarySuggest = Rx.Observable.fromEvent(input, 'keyup')
  .map(() => input.value)
  .filter(text => !!text)
  .distinctUntilChanged()
  .throttle(250)
  .flatMapLatest(searchWikipedia)
  .subscribe(
    results => {
      list = [];
      list.concat(results.map(createItem));
    },
    err => logError(err)
  );

此示例模拟在用户键入时,接收输入建议数据(autocomplete)的常见UI范例。

RxJS创建一个可观察序列,对输入的现有keyup事件建模

然后它在事件后放置了几个过滤器映射,让事件只有在发生了值的变化时才触发。 (每次按键都会触发 keyup 事件,包括用户按左右箭头移动光标,但这时输入文本并不会变化)。

接下来通过使用 throttle 操作符确保事件只在250毫秒的没有输入时才被触发。 (如果用户仍在键入字符,会被立即延迟请求,从而减少一次潜在的昂贵的请求)。

在以前编写的程序中,这种节流将通过定时器引入单独的回调。 这个计时器可能会抛出异常(某些定时器在运行中可能有大量的操作)。

一旦用户输入被过滤掉,就是执行查找的时间。 由于这通常是耗时的操作(例如对在世界的另一侧的服务器的请求),所以该操作本身也是异步的。

flatMap/selectMany 操作符可以方便地组合多个异步操作。它不仅可以合并请求成功后返回的值;它还可以跟踪在每个单独操作中发生的任何异常。

在传统的编程中,这将引入单独的回调异常捕获

如果用户在查询操作仍在进行时键入了新字符,我们不再需要看到该查询的结果。 因为用户已经键入更具体的词,看到旧的结果将会非常困惑。

flatMapLatest操作确保一旦检测到新的keyup,就忽略查询操作。

最后,我们订阅所得的可观察序列。 只有在这个时候我们的回调函数将被调用。 我们传递两个函数到subscribe 调用:

  1. 从我们的计算接收结果。
  2. 在执行的任何地方发生故障的情况下捕获异常。

2022/7/21

参考表:

静态方法

我想创建一个新的序列使用自定义逻辑Observable.create
像一个for循环Observable.generate
并随时间发射值Observable.generateWithRelativeTime
Observable.generateWithAbsoluteTime
它返回一个值Observable.return/just
多次Observable.repeat
这会抛出错误Observable.throw
完成了Observable.empty
从来没有做任何事情Observable.never
从事件Observable.fromEvent
它使用自定义函数来添加和删除事件处理程序Observable.fromEventPattern
来自一个ES6 PromiseObservable.fromPromise
它可迭代覆盖到数组中的值Observable.fromArray
对象键/值对Observable.pairs
异步元素Observable.for
数值范围内的值Observable.range
来自一个可迭代的数组或类似数组的对象的值Observable.from
来自参数Observable.of
根据定时器发出值Observable.interval
具有可选的初始延迟Observable.timer
不传参调用函数在特定的调度程序Observable.start
异步Observable.startAsync
取决于订阅时基于布尔条件Observable.if
f从一组预先设定的序列Observable.case
使用自定义逻辑Observable.defer
它取决于资源Observable.using
我想包装一个函数并产生一个序列的结果Observable.toAsync
它接受回调Observable.fromCallback
它接受Node.js回调Observable.fromNodeCallback
我想结合多个序列并且仅从产生值的序列中接收值Observable.amb
所有人都已经完成通知Observable.forkJoin
并输出所有这些值Observable.merge
为了不改变时重复使用最新值Observable.combineLatest
每个值只使用一次Observable.zip
通过订阅每个序列为了当前一个序列完成时Observable.concat
当另一个序列抛出错误时Observable.catch
不管先前的序列是完成还是抛出错误Observable.onErrorResumeNext
通过响应不同的值组合(连接微积分)Observable.when

实例操作符

使用现有的序列我想改变每个值map/select
我想从每个值拉一个属性pluck
我想在不影响值的情况下被通知值do/tap
doOnNext/tapOnNext
doOnError/tapOnError
doOnCompleted/tapOnCompleted
我想包含值基于自定义逻辑filter/where
从序列开头take
基于自定义逻辑takeWhile
从序列的末尾takeLast
直到另一个序列发射一个值或完成takeUntil
我想忽略值全部ignoreElements
从序列的开头skip
基于自定义逻辑skipWhile
从序列的末尾skipLast
直到另一个序列发出一个值skipUntil
与以前的值相同distinctUntilChanged
这(触发)太频繁throttle
我想计算总和这些值的sum
平均值average
使用自定义逻辑并且只输出最终值aggregate
reduce
并在计算出值时输出(每一步的)值scan
我想用元数据包装它的消息描述每个消息materialize
包括从最后一个价值以来的时间timeInterval
包括时间戳timestamp
经过一段时间的不活动我想抛出一个错误timeout
我想切换到另一个序列timeout
我想确保只有一个值并且如果存在多于或少于一个值则抛出错误single
并且如果没有值,则使用默认值singleOrDefault
我只想取第一个值并且如果没有值,则抛出错误first
并且如果没有值,则使用默认值firstOrDefault
在一段时间内sample
我只想取最后的值如果没有值,则报错last
并且如果没有值,则使用默认值lastOrDefault
我想知道它包含多少值count
我想知道它是否包含一个指定的值contains
我想知道条件是否满足只需要任一值满足any/some
需要所有值都满足all/every
我想把消息延迟一段特定的时间delay
基于自定义逻辑delayWithSelector
我想给值分组直到序列完成toArray
toMap
toSet
使用自定义逻辑作为数组buffer
作为序列window
根据特定大小分批作为数组bufferWithCount
作为序列windowWithCount
基于时间作为数组bufferWithTime
作为序列windowWithTime
基于时间或计数,以先发生者为准作为数组bufferWithTimeOrCount
作为序列windowWithTimeOrCount
基于一个指定的key直到序列完成groupBy
并控制每组的生命周期groupByUntil
我想为每个值开始一个新的序列并且并行地从所有序列中发出值flatMap/selectMany
并按顺序从每个序列中输出值concatMap/selectConcat
并在新值到达时取消先前的序列flatMapLatest/selectSwitch
并递归地为每个新值启动一个新的序列expand
并根据onNext,onError和onCompleted并行地从所有序列发出值flatMapObserver/selectManyObserver
并根据onNext,onError和onCompleted顺序地从所有序列发出值concatMapObserver/selectConcatObserver
我想把它与另一个结合起来两者都完成时发出通知forkJoin
我想执行复杂的操作,而不会打破流畅的调用let
我想在多个订阅者之间共享订阅使用特定的`subject`实现multicast
publish
share
并向未来订阅者提供最后的值publishLast
shareLast
并向未来订阅者重播默认值或最新值publishValue
shareValue
并向未来的订阅者重播n个值replay
shareReplay
发生错误时我想重新订阅retry
我想开始一个新序列catch
取决于错误catch
当完成时我想重新订阅repeat
我想开始一个新序列concat
当完成或抛出错误时我想开始一个新序列onErrorResumeNext
当完成,抛出错误或退订时我想执行一个函数finally
我想改变路由的调度程序调用`subscribe`(订阅)subscribeOn
消息observeOn
使用两个序列我想决定从哪个接收值取决于哪个序列先发出值amb
我想确定它们的值是否相等sequenceEqual
我想合并它们的值只有当第一个序列发射时,使用每个序列的最新值withLatestFrom
为了不改变时重复使用最新值combineLatest
每个值只使用一次zip
重复分享我选择的“生命周期”并通知每个组合join
并给每个“左”的序列的值给“右”的序列groupJoin
我想包含两者的值merge

reference:

简介 · RxJS中文文档https://robin-front.gitbooks.io/rxjs-doc-chinese/content/content/guidelines/introduction.html

 附:

编程范式:命令式编程、声明式编程、函数式编程和响应式编程。

命令式编程:命令式编程的主要思想是关注计算机执行的步骤,即一步一步告诉计算机先做什么再做什么。

//1. 声明变量
List<int> results = new List<int>();
//2. 循环变量
foreach(var num in Enumerable.Range(1,10))
{
    //3. 添加条件
    if (num > 5)
    {  
        //4. 添加处理逻辑
        results.Add(num);
        Console.WriteLine(num);
    }
}

声明式编程:声明式编程是以数据结构的形式来表达程序执行的逻辑。它的主要思想是告诉计算机应该做什么,但不指定具体要怎么做。

var nums = from num in Enumerable.Range(1,10) where num > 5 select num

函数式编程:主要思想是把运算过程尽量写成一系列嵌套的函数调用。

Enumerable.Range(1, 10).Where(num => num > 5).ToList().ForEach(Console.WriteLine);

响应式编程:响应式编程是一种面向数据流和变化传播的编程范式,旨在简化事件驱动应用的实现。响应式编程专注于如何创建依赖于变更的数据流并对变化做出响应。

IObservable<int> nums = Enumerable.Range(1, 10).ToObservable();

IDisposable subscription = nums.Where(num => num > 5).Subscribe(Console.WriteLine);

subscription.Dispose();
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
响应式编程(Reactive Programming)是一种面向数据流和变化传播的编程范式,它通过使用异步数据流来简化编程模型,提高程序的可读性、可维护性和可扩展性。响应式编程的主要思想是,当数据流发生变化时,系统会自动地对变化作出反应,保证系统的响应速度和稳定性。 响应式编程的原理可以总结为以下几个方面: 1. 基于事件驱动的编程模型:响应式编程采用事件驱动的编程模型,通过监听和响应事件来实现程序的功能。事件可以是用户输入、网络请求、定时器、传感器数据等。 2. 响应式数据流:响应式编程将数据和操作都看作是流,数据流是一种基于时间的序列数据,操作流是一种对数据流进行变换的操作序列。响应式编程中的数据流可以是无限的,也可以是有限的。 3. 响应式操作符:响应式编程提供了一系列的操作符来对数据流进行操作,这些操作符可以实现对数据流的过滤、映射、组合、合并等操作。 4. 异步编程:响应式编程采用异步编程的方式来处理数据流,可以使用回调函数、Promise、RxJS等异步编程库来实现异步处理。 5. 响应式框架:响应式编程可以应用于各种编程语言和平台,如JavaScriptJava、Swift等,同时也有一些响应式编程框架,如Angular、React、Vue等,可以帮助开发者更方便地实现响应式编程

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

董厂长

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值