函数响应式编程 FRP

FreeGuideOnline 最新 2026-07-12

// 伪代码示例:创建一个流,每1秒发出一个递增数字 const secondsStream = Observable.interval(1000).map(i => i + 1)


这个流的特性是:**惰性求值**——只有订阅时才会开始产生值;**可取消**——可以随时停止订阅,避免内存泄漏。

### 3. Operators(操作符)

操作符是 FRP 的灵魂,它们是**纯函数**,用于创建、转换、过滤、组合流。常见的操作符有:

| 操作符       | 作用                                   |
| ------------ | -------------------------------------- |
| `map`        | 将流中每个值变换为新值                |
| `filter`     | 只允许符合条件的值通过               |
| `merge`      | 将多个流合并成一个流                  |
| `switchMap`  | 将每个值映射到一个新流,并切换到最新 |
| `debounce`   | 防抖,只在事件停止一段时间后触发     |
| `buffer`     | 收集值,批量发出                     |
| `retry`      | 出错时重新订阅                       |

操作符组合就像在装配流水线:每一步都返回一个新的流,可以无限链式调用,保持数据不可变性。

### 4. Subscription(订阅)

订阅是连接数据生产者和消费者的桥梁。你通过 `subscribe` 方法传入三个回调函数(next, error, complete)来处理数据、错误和完成通知。

secondsStream.subscribe({ next: value => console.log(value), error: err => console.error('出错', err), complete: () => console.log('流结束') })


一旦订阅,就建立了数据通道;取消订阅则断开通道并释放资源。

## FRP 与传统异步编程的对比

| 传统方式(回调、Promise)              | 函数响应式编程                  |
| -------------------------------------- | ------------------------------- |
| 依赖嵌套,难以读懂和控制               | 声明式链式调用,清晰表达时间线  |
| 状态变更分散在各处                     | 状态变更集中为流,易于追踪      |
| 异步组合困难(如竞态、超时、重试)     | 用操作符轻松组合、转换异步流    |
| 手动管理取消(如 clearTimeout)        | 订阅与取消模型统一、安全        |
| 很多时候需要外部变量保存中间状态       | 数据通过管道流动,减少可变状态  |

例如,处理“输入框每次输入后延迟 300 毫秒请求搜索,忽略过时结果”这样的场景,用传统方式需要大量定时器和状态标记。而在 FRP 中只有短短一行:

searchInput$ = input$.pipe( debounceTime(300), distinctUntilChanged(), switchMap(term => searchApi(term)) )


**流自然地处理了时间维度**,这就是 FRP 的强大之处。

## FRP 解决的实际问题

- **异步竞态**:`switchMap` 自动取消旧的请求,保证最新结果。
- **多个事件的协调**:将多个按钮点击流、键盘流合并,生成复杂的交互。
- **实时数据更新**:WebSocket 推送与 UI 状态自动同步。
- **状态管理**:将应用中所有变更抽象成流,实现可预测的状态变化(如 Redux-Observable)。
- **动画与游戏**:处理游戏循环、鼠标轨迹、碰撞检测等连续数据。

## 主流 FRP 库与生态

| 语言/平台 | 代表性库                              |
| --------- | ------------------------------------- |
| JavaScript | RxJS、Bacon.js、Kefir.js、Most.js     |
| Kotlin     | kotlinx.coroutines.flow               |
| Swift      | Combine、ReactiveSwift                |
| Dart       | rxdart                                |
| Scala      | Monix、ZIO Stream                     |
| C++        | RxCpp                                 |

其中 **RxJS** 是目前使用最广泛的库,Angular 框架深度集成了它。但 FRP 思想远不止于某个库,它是一种解决问题的思维模型。

## 一个完整示例:搜索建议

下面用 RxJS 呈现搜索建议功能的实现(也可以替换为任何语言的类似库):

```javascript
import { fromEvent } from 'rxjs'; 
import { debounceTime, map, filter, distinctUntilChanged, switchMap, catchError } from 'rxjs/operators';
import { ajax } from 'rxjs/ajax';

const input = document.getElementById('search');
const input$ = fromEvent(input, 'input').pipe(
  map(e => e.target.value),
  filter(text => text.length > 2),
  debounceTime(300),
  distinctUntilChanged(),
  switchMap(term => ajax.getJSON(`/api/search?q=${term}`).pipe(
    catchError(() => [])
  ))
);

input$.subscribe(results => renderSuggestions(results));