当前位置: 首页 > news >正文

Nestjs框架: 理解 RxJS响应式编程的核心概念与实践

概述

  • RxJS 是一个用于处理异步和基于事件编程的库,其核心理念是使用可观察序列(Observable Sequences)来构建程序逻辑
  • 你可以把它看作是处理事件的“lodash”,类似于 lodash 对数组和对象的操作,RxJS 则是对事件流的操作

1 )学习资源推荐

目前中文社区中有几个关于 RxJS 的文档网站可供参考:

  • cn.rx.js.org 官方文档,内容详尽,结构清晰;

  • rx.nodejs.cn 中文社区翻译版,适合中文用户;

  • rxjs.tech 翻译质量相对较高,推荐使用。

  • rxjs.dev 官网

  • 需要注意的是,文档中某些功能在文档中标记为划线

  • 表示这些方法可能已被弃用,或在未来的版本中不再支持

2 )核心概念解析

  • 官方介绍中提到:RxJS 是一个使用可观察序列编写异步和基于事件程序的库

  • 这句话可能初看有些晦涩,但其核心聚焦点在于两个方面:

    • 可观察序列(Observable Sequences)
    • 异步/事件处理能力
  • 为了更好地理解 RxJS,我们需要先了解其几个核心类型:

    • Observable(可观察对象):类似于 Promise,但它可以发出多个值,而不是只能解决一次;
    • Observer(观察者):用于监听 Observable 发出的值;
    • Subject(主体):一种特殊的 Observable,用于广播事件;
    • Operators(操作符):如 map、filter、reduce 等,用于处理事件流;
    • Scheduler(调度器):控制异步任务的执行方式,属于进阶概念。
  • 这些核心类型和操作符构成了 RxJS 的基础,使得我们可以将异步事件当作集合一样进行操作和处理

3 )实践示例:从事件监听到流式处理

  • 我们以一个简单的 DOM 事件监听为例,来看看 RxJS 如何处理事件

  • 传统的写法是:

    document.addEventListener('click', () => {// 处理点击事件
    });
    
  • 而使用 RxJS 后:

    import { fromEvent } from 'rxjs';
    fromEvent(document, 'click').subscribe(() => { console.log('Clicked!') });
    
  • 这里的 fromEvent 返回一个 Observable,通过 subscribe 方法进行订阅

  • 你也可以传入一个 Observer 对象,它包含三个回调方法:

    • next():每次事件触发时调用;
    • error():发生错误时调用;
    • complete():流结束时调用。
  • 这种写法不仅结构清晰,而且支持链式调用,便于管理状态

4 )操作符与流式控制:Purity 与 Flow

4.1 Purity

  • Purity(纯净性) 是 RxJS 的一大优势,它允许我们使用纯函数来生成值
  • 创建一个不纯的函数,举例,通常会弄乱你的状态
    let count = 0;
    document.addEventListener('click', () => console.log(`Clicked ${++count} times`));
    
  • 例如,我们可以使用 scan 操作符来实现点击计数,可以隔离状态
    import { fromEvent, scan } from 'rxjs';
    import { scan } from 'rxjs/operators';fromEvent(document, 'click').pipe(scan((count) => count + 1, 0)).subscribe(count => console.log(`Clicked ${count} times`));
    
  • 在这个例子中,scan 类似于数组的 reduce 方法,每次点击都会将计数值递增,初始值是 0
  • 它可以把状态量在链式写法里面执行,不需要再外层单独定义了

4.2 Flow

Flow(流动) 是 RxJS 中另一个核心概念,它表示事件流的传递与变换
我们可以使用 pipe 函数将多个操作符串联起来,实现复杂的事件处理逻辑:
它把嵌套的函数调用变成了一种管道内参数传递的方式

下面是使用纯 JavaScript 实现“最多允许每秒单击一次”的方式:

let count = 0;
let rate = 1000;
let lastClick = Date.now() - rate;
document.addEventListener('click', () => {if (Date.now() - lastClick >= rate) {console.log(`Clicked ${++count} times`);lastClick = Date.now();}
});

使用 RxJS:

import { fromEvent, throttleTime, scan } from 'rxjs';fromEvent(document, 'click').pipe(throttleTime(1000),scan((count) => count + 1, 0)).subscribe((count) => console.log(`Clicked ${count} times`));
  • 这里本质上是一个节流操作,这里的 pipe 方法让代码更具有可读性
  • 将嵌套的函数调用转化为参数传递的方式,使得事件流的处理逻辑更加清晰
  • 操作符中的管道文档 operators#piping

5 ) Values 与值的处理

Values(值转换) 是 RxJS 中处理流中数据的方式
可以通过你的 observables 传来的值进行转换

以下是使用纯 JavaScript 来为每次单击增加当前鼠标 x 位置的方法

let count = 0;
const rate = 1000;
let lastClick = Date.now() - rate;
document.addEventListener('click', (event) => {if (Date.now() - lastClick >= rate) {count += event.clientX;console.log(count);lastClick = Date.now();}
});

使用 RxJS之后

import { fromEvent, throttleTime, map, scan } from 'rxjs';fromEvent(document, 'click').pipe(throttleTime(1000),map((event) => event.clientX),scan((count, clientX) => count + clientX, 0)).subscribe((count) => console.log(count));
  • 这里使用了 map 操作符提取事件中的值:

  • RxJS 提供了大量操作符

  • 这些操作符都可在官方文档的 Operators 分类中找到详细说明

6 ) 总结

RxJS 的几个核心概念

  • Observable、Observer、Subject、Operator、Scheduler
  • 如何使用 fromEvent 创建事件流
  • 如何通过 pipe 串联操作符处理事件流
  • 如何使用 scan、map、filter、throttleTime 等操作符进行事件处理
  • RxJS 与传统回调方式的区别:链式结构、状态管理、可维护性增强
http://www.dtcms.com/a/288124.html

相关文章:

  • C++中的虚继承
  • 思维链(CoT)技术全景:原理、实现与前沿应用深度解析
  • Edge浏览器设置网页自动翻译
  • 从随机数值到特征检测器的学习与更新
  • [硬件电路-37]:模拟电路、数字电路与计算软件信号处理的全方位比较
  • 暑假--作业3
  • 物联网系统中的可视化大屏定义
  • VSCode - VSCode 查找中文字符
  • 『 C++ 入门到放弃 』- AVL树
  • OpenCV 官翻 1 -介绍、安装、功能概览、核心操作
  • Streamlit 官翻 5 - 部署、社区云 Deploy
  • Linux内核空间的布局
  • 前端面试专栏-工程化:26.性能优化方案(加载优化、渲染优化)
  • 《Qt5串口开发》搭建跨平台通信系统
  • “外卖大战”正在改变国内“大零售”
  • 数据增强和微调
  • Codeforces Round 1037 (Div. 3)
  • windows docker-02-docker 最常用的命令汇总
  • uniapp props、$ref、$emit、$parent、$child、$on
  • 【数据结构】栈(stack)
  • xss-labs1-8题
  • ubuntu24 ros2 jazzy
  • OpenVINO使用教程--图像增强算法DarkIR
  • 华为擎云L420安装LocalSend
  • Oracle为什么需要临时表?——让数据处理更灵活
  • LeetCode 322. 零钱兑换 LeetCode 279.完全平方数 LeetCode 139.单词拆分 多重背包基础 56. 携带矿石资源
  • 【补题】Codeforces Round 958 (Div. 2) D. The Omnipotent Monster Killer
  • 窗口(6)-QMessageBox
  • ctf.show-web习题-web4-flag获取详解、总结
  • 动态规划——状压DP经典题目