各位观众,早上好!今天咱们来聊聊JavaScript里一个挺酷的东西——Observable,也就是可观察对象。别被这名字吓到,其实它就是响应式编程的核心,能让你的代码像打了鸡血一样,对变化超级敏感。
Observable:变化世界的观察者
想象一下,你是个侦探,专门盯着某个嫌疑人(数据源)。嫌疑人一有风吹草动(数据变化),你就立马知道,然后做出反应(执行代码)。Observable干的就是这事儿。它像一个“观察者”,时刻监听着数据源的变化,一旦有新数据产生,就通知它的“订阅者”去处理。
RxJS:Observable的豪华套餐
在JavaScript世界里,实现Observable最流行的工具就是RxJS(Reactive Extensions for JavaScript)。RxJS提供了一套完整的API,让你创建、转换、组合和管理Observable变得非常简单。你可以把RxJS想象成一个Observable的豪华套餐,里面包含了各种口味的Observable,以及各种调料(操作符),供你随意搭配。
Observable的核心概念
- Observable (可观察对象): 代表一个数据流,可以随着时间的推移发出多个值。
- Observer (观察者): 监听Observable发出的值,并执行相应的操作。
- Subscription (订阅): 表示Observable和Observer之间的连接,可以用来取消订阅。
- Operator (操作符): 用于转换、过滤、组合Observable发出的值。
创建你的第一个Observable
最简单的Observable就是直接发出几个值的例子:
import { Observable } from 'rxjs';
const myObservable = new Observable(subscriber => {
subscriber.next(1); // 发出值 1
subscriber.next(2); // 发出值 2
subscriber.next(3); // 发出值 3
subscriber.complete(); // 完成,不再发出值
});
myObservable.subscribe({
next: value => console.log('收到值:', value),
error: err => console.error('发生错误:', err),
complete: () => console.log('完成!')
});
// 输出:
// 收到值: 1
// 收到值: 2
// 收到值: 3
// 完成!
这段代码创建了一个Observable,它会依次发出1、2、3三个值,然后完成。subscribe
方法用于订阅这个Observable,并传入一个Observer对象。Observer对象包含三个方法:
next
: 当Observable发出一个新值时调用。error
: 当Observable发生错误时调用。complete
: 当Observable完成时调用。
Observable的订阅和取消订阅
订阅Observable会返回一个Subscription
对象,你可以用它来取消订阅,停止接收Observable发出的值。
import { Observable } from 'rxjs';
const intervalObservable = new Observable(subscriber => {
let count = 0;
const intervalId = setInterval(() => {
subscriber.next(count++);
}, 1000);
return () => { // 返回取消订阅时执行的函数
clearInterval(intervalId);
console.log('停止计时器');
};
});
const subscription = intervalObservable.subscribe({
next: value => console.log('收到值:', value)
});
setTimeout(() => {
subscription.unsubscribe(); // 取消订阅
}, 3500);
// 输出 (大约):
// 收到值: 0
// 收到值: 1
// 收到值: 2
// 停止计时器
这个例子创建了一个每秒发出一个值的Observable。unsubscribe
方法用于取消订阅,停止计时器。 注意 unsubscribe
方法执行时,会调用 Observable 构造函数中返回的函数。
操作符:Observable的变形金刚
RxJS提供了大量的操作符,用于转换、过滤、组合Observable发出的值。这些操作符就像变形金刚一样,可以把Observable变成各种各样的形态。
map
: 转换Observable发出的值。filter
: 过滤Observable发出的值。merge
: 将多个Observable合并成一个。debounceTime
: 在一段时间内只发出最后一个值。take
: 只发出指定数量的值。
import { fromEvent } from 'rxjs';
import { map, filter, debounceTime } from 'rxjs/operators';
const inputElement = document.getElementById('myInput'); // 假设页面上有个id为myInput的input元素
const keyupObservable = fromEvent(inputElement, 'keyup');
keyupObservable.pipe(
map((event: any) => event.target.value), // 获取输入框的值
debounceTime(500), // 500毫秒内只发出最后一个值
filter(value => value.length > 2) // 过滤掉长度小于等于2的值
).subscribe(value => {
console.log('搜索:', value);
});
这个例子监听输入框的keyup
事件,获取输入框的值,等待500毫秒,然后过滤掉长度小于等于2的值,最后输出搜索结果。pipe
方法用于将多个操作符串联起来,形成一个操作符管道。
Hot Observable vs. Cold Observable:冰与火之歌
Observable有两种类型:Hot Observable和Cold Observable。它们的主要区别在于数据的产生方式和共享方式。这两种 Observable 就像冰和火一样,特性截然不同。
特性 | Hot Observable | Cold Observable |
---|---|---|
数据产生时间 | 在订阅之前就已经开始产生数据 | 在订阅时才开始产生数据 |
数据共享 | 所有订阅者共享同一份数据流 | 每个订阅者都获得一份独立的数据流 |
数据源 | 通常是外部数据源,如DOM事件、WebSocket等 | 通常是内部数据源,如数组、函数等 |
适用场景 | 实时数据、事件流、需要共享状态的场景 | 请求数据、执行计算、不需要共享状态的场景 |
举例 | DOM事件(如fromEvent ),WebSocket,Subject |
new Observable ,from ,ajax (某些情况下) |
可以多次订阅吗 | 可以,并且所有订阅者共享同一份数据 | 可以,但是每次订阅都会重新开始产生数据 |
订阅前的数据呢 | 如果没有订阅者,数据可能会丢失 | 如果没有订阅者,数据不会产生 |
Cold Observable:你的私人定制
Cold Observable就像一个为你私人定制的冰淇淋,只有你点单(订阅)的时候,才会开始制作(产生数据)。每个订阅者都会得到一份全新的冰淇淋,互不干扰。
import { Observable } from 'rxjs';
const coldObservable = new Observable(subscriber => {
console.log('开始生成数据');
subscriber.next(Math.random()); // 每次订阅都会生成新的随机数
subscriber.complete();
});
coldObservable.subscribe(value => console.log('订阅者1:', value));
coldObservable.subscribe(value => console.log('订阅者2:', value));
// 输出:
// 开始生成数据
// 订阅者1: 0.123456789 (随机数)
// 开始生成数据
// 订阅者2: 0.987654321 (另一个随机数)
每次订阅coldObservable
,都会重新执行Observable的构造函数,生成新的随机数。这意味着每个订阅者都会得到不同的值。
Hot Observable:大家的公共财产
Hot Observable就像一个公共电视台,节目(数据)一直在播放,不管有没有观众(订阅者)。如果你打开电视(订阅),就能看到正在播放的节目。如果你错过了之前的节目,就错过了,无法回看。
import { Subject } from 'rxjs';
const hotObservable = new Subject();
hotObservable.subscribe(value => console.log('订阅者1:', value));
hotObservable.next(1); // 在订阅者1订阅之后发出值
hotObservable.subscribe(value => console.log('订阅者2:', value)); // 订阅者2订阅时,值1已经发出
hotObservable.next(2); // 订阅者1和订阅者2都能收到值
// 输出:
// 订阅者1: 1
// 订阅者1: 2
// 订阅者2: 2
Subject
是RxJS中的一种特殊的Observable,它可以同时作为Observable和Observer使用。hotObservable
在订阅者1订阅之后才开始发出值,所以订阅者2只能收到值2,而收不到值1。
将Cold Observable转换为Hot Observable
有时候,我们需要将Cold Observable转换为Hot Observable,以便多个订阅者共享同一份数据流。可以使用share
操作符来实现:
import { Observable, interval } from 'rxjs';
import { take, share } from 'rxjs/operators';
const coldObservable = interval(1000).pipe(take(5)); // 每秒发出一个值,最多发出5个
const hotObservable = coldObservable.pipe(share());
hotObservable.subscribe(value => console.log('订阅者1:', value));
setTimeout(() => {
hotObservable.subscribe(value => console.log('订阅者2:', value));
}, 2000);
// 输出 (大约):
// 订阅者1: 0
// 订阅者1: 1
// 订阅者1: 2
// 订阅者2: 2
// 订阅者1: 3
// 订阅者2: 3
// 订阅者1: 4
// 订阅者2: 4
share
操作符会将coldObservable
转换为Hot Observable,多个订阅者共享同一个计时器。订阅者2在2秒后订阅,只能收到从2开始的值。
总结
Observable是响应式编程的核心,RxJS提供了丰富的API来创建、转换和管理Observable。Hot Observable和Cold Observable的主要区别在于数据的产生方式和共享方式。选择合适的Observable类型,可以让你更好地处理异步数据流,构建响应式应用程序。
总的来说,Observable 和 RxJS 的世界非常广阔,今天我们只是浅尝辄止。希望通过今天的讲解,你能对 Observable 有一个初步的了解,并能在实际项目中运用它来解决问题。 记住,多实践,多思考,你也能成为 Observable 大师!
祝大家学习愉快!