RxJS 系列 – Custom Operator
前言
虽然 RxJS 提供了非常多的 Operators. 但依然会有不够用的时候. 这时就可以自定义 Operator 了.
Operator Is Just a Function Observable => Observable
Operator 只是一个函数.
timer(1000).pipe(obs => obs).subscribe();
它接收一个 Observable 返回一个 Observable.
Operator 都是放在管道 (pipe) 内的, 所以从源头 Observable 一直传递下去.
每一个 Operator 接收 upstream(上游) 的 Observable 并且返回一个 Observable (通常是新的一个) 给 downstream(下游).
每一个 Operator 就是对发布值的加工处理.
Standard Operator
source 源头
首先我们有个源头
const source = timer(1000);
每一秒发布一次
Upstream / Downstream Observable
接着搞一个 pipe 和 operator.
const source = timer(1000);
source
.pipe(upstreamObs => {
console.log(upstreamObs === source); // true const downstreamObs = new Observable();
return downstreamObs;
})
.subscribe(v => console.log(v));
可以看到 operator 函数接收的就是 upstream 的 Observable, 然后返回一个新的 Observable 到 downstream.
Connect Upstream and Downstream
downstream to upstream
当 downstream 被 subscribe, 我们 subscribe upstream
当 downstream 被 unsubscribe, 我们 unsubscribe upstream
const source = timer(1000);
source
.pipe(upstreamObs => {
const downstreamObs = new Observable(subscriber => { // 当 downstream Observable 被订阅以后, 我们才订阅 upstream Observable
const upstreamSub = upstreamObs.subscribe(); return () => {
// 当 downstream 被退订以后, 我们也要退订 upstream Observable
upstreamSub.unsubscribe();
}; }); return downstreamObs;
})
.subscribe(v => console.log(v));
upstream to downstream
当接收到 upstream 发布时, 我们也发布到 downstream
const downstreamObs = new Observable(subscriber => {
const upstreamSub = upstreamObs.subscribe({
next: upstreamValue => {
const downStreamValue = upstreamValue + 'downstream value'; // decorate value for downstream
subscriber.next(downStreamValue); // 发布到 downstream
},
error: error => subscriber.error(error), // 转发去 downstream
complete: () => subscriber.complete(), // 转发去 downstream
});
return () => {
upstreamSub.unsubscribe();
};
});
小结
从上面几招可以看出来, 主要就是 upstream 和 downstream 中间的关系.
如何订阅, 退订, 发布 value 等等. 上面只是给一个简单直观的例子. 真实场景中, upstream 和 downstream 发布时机, 往往是不一致的 (比如 upstream 发布 1 次后, downstream 可能发布多次)
Operator with Config
只要把 Operator 包装成工厂函数就可以支持 Config 了
function myOperator(config: { dependObs: Observable<unknown> }): OperatorFunction<number, string> {
const { dependObs }= config;
return upstreamObs => {
const downstreamObs = new Observable<string>(subscriber => {
// 订阅 depend
const dependObsSub = dependObs.subscribe(() => console.log('do something'));
const upstreamSub = upstreamObs.subscribe({
next: upstreamValue => {
const downStreamValue = upstreamValue + 'downstream value'; // decorate value for downstream
subscriber.next(downStreamValue); // 发布到 downstream
},
error: error => subscriber.error(error), // 转发去 downstream
complete: () => {
// 释放 depend
dependObsSub.unsubscribe();
subscriber.complete();
},
});
return () => {
upstreamSub.unsubscribe();
// 释放 depend
dependObsSub.unsubscribe();
};
});
return downstreamObs;
};
}
const source = timer(1000);
const dependObs = new Subject();
source.pipe(myOperator({ dependObs })).subscribe(v => console.log(v));
一个 Factory 函数, 通过配置生产 Operator。
OperatorFunction 定义了 Operator 接口, 它是加入了泛型的 Observable => Observable.
如果 upstream 和 downstream 的类型是一样的话可以用它的简化版本 MonoTypeOperatorFunction<T>

Unsubscribe dependency stream
比如说像 takeUntil operator
const obs = timer(0, 1000); // 0..1..2..3..4..5
obs.pipe(takeUntil(timer(3000))).subscribe(v => console.log(v)); // 0..1..2
它内部会 subscribe timer$,timer$ 就是它的 dependency observable。
如果在还没有到 3 秒的时候,这个 observable 就被 unsubscribe 了,那 takeUntil operator 内部就要提前去 unsubscribe timer$。
同理,如果这个 observable 在还没有到 3 秒的时候,就被 error 或 complete 了,那 takeUntil operator 内部也需要提前去 unsubscribe timer$。
总之,我们在设计 operator 时,一定要顾虑到 upstream 的 error / complete 和 downstream 的 unsubscribe 对 dependency observable 的影响。
RxJS 系列 – Custom Operator的更多相关文章
- [RxJS] Error handling operator: catch
Most of the common RxJS operators are about transformation, combination or filtering, but this lesso ...
- [转]VS Code 扩展 Angular 6 Snippets - TypeScript, Html, Angular Material, ngRx, RxJS & Flex Layout
本文转自:https://marketplace.visualstudio.com/items?itemName=Mikael.Angular-BeastCode VSCode Angular Typ ...
- [RxJS] Learn How To Use RxJS 5.5 Beta 2
The main changes is about how you import rxjs opreators from now on. And introduce lettable opreator ...
- RxJS——调度器(Scheduler)
调度器 什么是调度器?调度器是当开始订阅时,控制通知推送的.它由三个部分组成. 调度是数据结构.它知道怎样在优先级或其他标准去存储和排队运行的任务 调度器是一个执行上下文.它表示任务在何时何地执行(例 ...
- C++复制对象时勿忘每一部分
现看这样一个程序: void logCall(const string& funcname) //标记记录 { cout <<funcname <<endl; } cl ...
- Flink - DataStream
先看例子, final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment(); D ...
- 重载new delete操作符是怎么调用的
自定义的new操作符是怎么对英语new 一个对象的?自定义的delete操作符什么情况下得到调用?new一个对象时出现异常需要我操心内存泄露吗?下面的一个例子帮我们解开所有的疑惑. 1. 调用规则 ...
- 算法竞赛向 C++ Standard Library 使用速查
因网络上 STL 教程大多零散且缺乏严谨性,本文对算法竞赛所需 C++ Standard Library 做了一个较为全面的总结. 全文主要参考以下文档: Containers library - c ...
- [RxJS] Create a Reusable Operator from Scratch in RxJS
With knowledge of extending Subscriber and using source.lift to connect a source to a subscriber, yo ...
- [RxJS] Creation operator: of()
RxJS is a lot about the so-called "operators". We will learn most of the important operato ...
随机推荐
- WebGL加载本地模型
前言 大部分的webgl框架,比如threejs和babylon等,都可以加载obj和gltf模型. 我们的引擎,基于three封装,同样有加载模型的loader,因此加载obj和gltf模型也是很简 ...
- TypeScript 学习笔记 — 交叉类型、条件类型和条件分发(八)
目录 交叉类型 条件类型 1. 直接传入判断的条件 2. 把条件当成泛型传入 3. 多重条件判断 4. 判断接口中的类型 extends 父子关系(类型等级) 条件分发机制 实际开发中如何避免? &l ...
- 操作系统|C语言模拟实现首次适应和最佳适应两种内存分配算法以及内存回收
两种算法 首次适应 首次适应算法从空闲分区表的第一个表目起查找该表,把最先能够满足要求的空闲区分配给作业,这种方法目的在于减少查找时间.为适应这种算法,空闲分区表(空闲区链)中的空闲分区要按地址由低到 ...
- 人工智能时代,前端全栈成就独立开发工程师 next.js 开发实战
next 可以服务端渲染,可以客户端渲染,让前端同事更有性价比,让我们做得可以更多 由于next.js 是基础于react 所以在正式学习next.js 之前我们了解一下react 什么叫模块 ,就 ...
- Python Pillow(PIL 第三方模块)和 cv2 (opencv第三方模块)对图片的 resize 操作 (缩放图片大小)
PIL 模块的 resize 操作: 1. 从文件中读取图片,然后 resize 大小: import matplotlib.pyplot as plt import numpy as np ...
- (续) gym atari游戏的环境设置问题:Breakout-v0, Breakout-v4, BreakoutNoFrameskip-v4和BreakoutDeterministic-v4的区别
根据前文(https://www.cnblogs.com/devilmaycry812839668/p/14665072.html)我们知道: 首先是v0和v4的区别:带有v0的env表示会有25%的 ...
- anaconda运行install命令报错:Caused by SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:997)'
运行命令: conda install mpi4py 报错: Retrieving notices: ...working... ERROR conda.notices.fetch:get_chann ...
- lua环境配置与编译
1.背景 2.安装lua 官方下载地址:https://joedf.ahkscript.org/LuaBuilds/ 下载后解压即可 解压后: 配置环境变量: 检查是否安装成功: 如果能输出版本号,则 ...
- ArgoWorkflow 教程(一)--DevOps 另一选择?云原生 CICD 初体验
本文主要记录了如何使用 ArgoWorkflow 构建流水线,以及 ArgoWorkflow 中 的 Workflow.Template 等概念模型. 本文主要分析以下问题: 1)如何创建流水线 2) ...
- keycloak~关于社区登录的过程说明
keycloak将第三方登录(社区登录)进行了封装,大体主要会经历以下三个过程: 打开社区认证页面,输入账号密码或者扫码,完成社区上的认证 由社区进行302重定向,回到keycloak页面 keycl ...