24、深入理解 RxJS 中可观察对象的冷热特性及副作用影响

深入理解 RxJS 中可观察对象的冷热特性及副作用影响

1. WebSocket 服务端与客户端的实现

在 RxJS 中,我们可以使用 Rx.Observable.fromEvent 来处理 WebSocket 服务端和客户端的通信。以下是服务端的代码示例:

Rx.Observable.fromEvent(wsServer, 'request')
   .map(request => request.accept(null, request.origin))
   .subscribe(connection => {
      connection.sendUTF(JSON.stringify({ msg:'Hello Socket' }));
   });

运行服务端的步骤如下:
1. 确保已经安装了 Node.js。
2. 在命令行中执行 node server.js 启动服务。

客户端代码示例如下:

const websocket = new WebSocket('ws://localhost:1337');
Rx.Observable.fromEvent(websocket, 'message')
  .map(msg => JSON.parse(msg.data))
  .pluck('msg')
  .subscribe(console.log);

这个客户端代码的执行流程如下:
1. 创建一个 WebSocket 连接到 ws://localhost:1337
2. 使用 Rx.Observable.fromEvent 监听 message 事件。
3. 将接收到的消息数据解析为 JSON 对象。
4. 提取 msg 属性。
5. 将提取的值打印到控制台。

2. 热可观察对象的数据丢失问题

使用 RxJS 与 WebSocket 创建的是热可观察对象,这意味着订阅时不会重新播放之前发出的所有消息,而是从订阅后开始推送消息。例如:

Rx.Observable.timer(3000)
  .mergeMap(() => Rx.Observable.fromEvent(websocket, 'message')) 
  .map(msg => JSON.parse(msg.data))
  .pluck('msg')  
  .subscribe(console.log);

在这个代码中,我们设置了一个 3 秒的延迟来模拟页面初始化的延迟。如果在这 3 秒内 WebSocket 已经开始发送消息,那么这个可观察对象可能会错过这些消息。这种时间依赖可能会受到多种因素的影响,如应用是否从缓存加载、用户系统的处理资源、网络延迟以及页面上的动画等。

3. 重放(Replay)与重新订阅(Resubscribe)的区别

在 RxJS 中,重放和重新订阅是两个重要的概念:
- 重放(Replay) :是指将相同的事件序列重新发送给每个订阅者,实际上是重新播放整个序列。例如,从 Promise 创建的可观察对象,通过重试或添加新的订阅者来重放时,已完成的 Promise 不会再次调用,而是直接返回值或错误。但重放可能需要大量内存来存储要重新发送的流内容,因此不建议用于像鼠标点击这样的无限事件发射器。
- 重新订阅(Resubscribe) :会重新创建相同的管道并重新执行产生事件的代码。如果可观察对象的管道保持纯净,那么可以保证所有订阅者对于相同的输入会收到相同的事件。

以下是一个重放的示例代码:

const p = new Promise((resolve, reject) => {
  setTimeout(() =>{
    let isAtAfter10pm = moment().hour() >= 20;
    if(isAtAfter10pm) {
      reject(new Error('Too late!')); 
    }
    else {
      resolve('Success!');
    }
  }, 5000);
});
const promise$ = Rx.Observable.fromPromise(p);
promise$.subscribe(val => console.log(`Sub1 ${val}`));
// ... after 10 pm ...//
promise$.subscribe(val => console.log(`Sub2 ${val}`));

无论订阅时间如何,所有订阅者都会收到相同的值,这是因为 Promise 的特性,第二次订阅时不会再次执行 Promise 的主体代码,这就是重放。

重新订阅的示例代码如下:

const interval$ = Rx.Observable.create(observer => {
  let i = 0;
  observer.next('Starting interval...'); 
  let intervalId = setInterval(() => {
    let isAtAfter10pm = moment().hour() >= 20;
    if(isAtAfter10pm) {
      clearInterval(intervalId);
      observer.complete();
    }
    observer.next(`Next ${i++}`); 
  }, 1000);  
});
// ... before 10 pm ... //
const sub1 = interval$.subscribe(val => console.log(`Sub1 ${val}`));
// ... after 10 pm ... //
const sub2 = interval$.subscribe(val => console.log(`Sub2 ${val}`));

在这个例子中,重新订阅会重新执行代码逻辑,不同的订阅者根据订阅时间可能会收到不同的事件。如果在 10 点之后订阅,可能不会收到任何事件。

4. 副作用对重放和重新订阅的影响

直接在代码中使用时间是明显的副作用,因为时间是全局且不断变化的。热可观察对象在这方面不如冷可观察对象纯粹,冷可观察对象应该向任何订阅者重新发射(或重放)所有项目。例如,在使用 retry() 操作符时,如果直接在 Promise 上使用,可能会出现意外结果。因为 Promise 是热可观察对象,一旦处于已解决或已拒绝状态,就不会再次执行,所以重试操作是无效的。为了解决这个问题,我们可以将 Promise 的创建包装在另一个可观察对象中,使其可以重试。

5. 可观察对象冷热特性的判断

可观察对象的冷热特性可以根据生产者来判断:
- 冷可观察对象 :当可观察对象创建生产者时,它是冷的。例如:

const cold$ = new Rx.Observable(observer => {
   const producer = new Producer();
   // ...Observer listens to producer, 
   //    producer pushes events to the observer... 
   producer.addEventListener('some-event', e => observer.next(e));   
   return () => producer.dispose(); 
});

在这个例子中, producer 是在可观察对象的上下文中创建的,每个订阅者都会获得自己的 producer 副本,这是一种单播(unicast)通信。
- 热可观察对象 :当可观察对象封闭生产者时,它是热的。例如:

const producer = new Producer();
const hot$ = new Rx.Observable(observer => {
   // ...Observer listens to producer, 
   //    and pushes events onto it... 
   producer.addEventListener('some-event', e => observer.next(e));
});

在这个例子中, producer 是在可观察对象外部创建的,其生命周期独立于可观察对象,多个订阅者可以共享同一个 producer

以下是冷热可观察对象的对比表格:
| 特性 | 冷可观察对象 | 热可观察对象 |
| ---- | ---- | ---- |
| 生产者创建位置 | 可观察对象内部 | 可观察对象外部 |
| 订阅者与生产者关系 | 每个订阅者有自己的生产者副本 | 多个订阅者共享同一个生产者 |
| 重放特性 | 可以重放之前的事件 | 一般不重放之前的事件 |

6. 总结

理解 RxJS 中可观察对象的冷热特性以及重放和重新订阅的区别对于编写高效、可靠的代码至关重要。在处理资源昂贵的生产者时,如远程 HTTP 调用或 WebSocket,共享生产者可以节省资源;而在需要独立处理数据时,使用冷可观察对象可以确保每个订阅者有自己的处理流程。同时,要注意副作用对可观察对象行为的影响,避免因时间依赖等问题导致数据丢失或结果不一致。

通过以上的介绍和示例代码,我们可以更好地掌握 RxJS 中可观察对象的特性和使用方法,从而在实际项目中更加灵活地应用。

深入理解 RxJS 中可观察对象的冷热特性及副作用影响

7. 改变可观察对象的冷热特性的实际应用

在实际应用中,我们经常会遇到需要改变可观察对象冷热特性的场景。例如,在处理股票行情数据时,我们希望能够高效地获取和处理数据,同时避免不必要的资源浪费。下面我们通过一个具体的例子来详细说明如何改变可观察对象的冷热特性。

首先,我们定义一个获取股票报价的请求函数:

const requestQuote$ = symbol => 
  Rx.Observable.fromPromise(
    ajax(webservice.replace(/\$symbol/, symbol))
  )
  .retry(3)
  .map(response => response.replace(/"/g, ''))
  .map(csv); 

在这个代码中,我们使用 fromPromise 创建了一个可观察对象。但是,由于 Promise 是热可观察对象,直接在 Promise 上使用 retry 操作符是无效的。因为 Promise 一旦处于已解决或已拒绝状态,就不会再次执行。

为了解决这个问题,我们将 Promise 的创建包装在另一个可观察对象中:

const fetchDataInterval$ = symbol => twoSecond$
  .mergeMap(() => requestQuote$(symbol))
  .retry(3)
  .catch(err => Rx.Observable.throw(
    new Error('Stock data not available. Try again later!')
  ));    

在这个代码中,我们在 mergeMap 操作符内部创建了一个新的 Promise。 retry 操作符会重新订阅投影的可观察对象,而不是直接订阅 fromPromise 创建的可观察对象。这样,我们就改变了可观察对象的冷热特性,使其表现得像冷可观察对象,从而实现了重试功能。

这个过程的流程图如下:

graph TD;
    A[twoSecond$ 流] --> B[mergeMap 操作];
    B --> C[requestQuote$(symbol) 可观察对象];
    C --> D[retry(3) 重试操作];
    D --> E{是否成功};
    E -- 是 --> F[输出数据];
    E -- 否 --> G[catch 错误处理];
    G --> H[抛出错误信息];
8. 冷热可观察对象在不同场景下的选择

在实际开发中,我们需要根据不同的场景选择合适的可观察对象类型。下面我们通过一个表格来对比不同场景下冷热可观察对象的优缺点:
| 场景 | 冷可观察对象 | 热可观察对象 |
| ---- | ---- | ---- |
| 处理独立数据 | 每个订阅者有自己的生产者副本,数据处理相互独立,不会相互影响。例如,处理从生成器函数产生的一组对象时,每个订阅者可以独立处理自己的副本。 | 多个订阅者共享同一个生产者,可能会导致数据处理相互干扰。 |
| 资源共享 | 每个订阅者都需要创建自己的生产者,可能会消耗大量资源。例如,每次订阅都需要发起一个新的 HTTP 请求。 | 多个订阅者可以共享同一个生产者,节省资源。例如,多个订阅者可以共享同一个 WebSocket 连接。 |
| 数据重放 | 可以重放之前的事件,确保每个订阅者都能看到完整的数据序列。 | 一般不重放之前的事件,订阅较晚的订阅者可能会错过部分数据。 |

9. 避免副作用对可观察对象的影响

为了避免副作用对可观察对象的影响,我们可以采取以下几个措施:
1. 减少时间依赖 :尽量避免在代码中直接使用时间作为判断条件,因为时间是全局且不断变化的,容易引入副作用。如果确实需要使用时间,可以考虑使用定时器或其他方式来模拟时间的变化。
2. 封装副作用 :将副作用封装在独立的函数或模块中,避免直接在可观察对象的管道中使用。例如,将与时间相关的操作封装在一个函数中,在需要时调用该函数。
3. 使用纯函数 :在可观察对象的管道中尽量使用纯函数,纯函数不会产生副作用,输入相同的参数会返回相同的结果。这样可以确保可观察对象的行为更加稳定和可预测。

10. 总结与展望

通过本文的介绍,我们深入了解了 RxJS 中可观察对象的冷热特性、重放和重新订阅的区别,以及副作用对它们的影响。在实际开发中,我们应该根据具体的场景选择合适的可观察对象类型,并采取相应的措施来避免副作用的影响。

未来,随着前端技术的不断发展,RxJS 作为一种强大的响应式编程库,将会在更多的领域得到应用。我们可以进一步探索 RxJS 的高级特性,如多播、缓存等,以提高代码的性能和可维护性。同时,我们也可以将 RxJS 与其他前端框架结合使用,构建更加复杂和高效的应用程序。

总之,掌握 RxJS 中可观察对象的特性和使用方法是前端开发者必备的技能之一。希望本文能够帮助你更好地理解和应用 RxJS,在实际项目中发挥更大的作用。

内容概要:本文介绍了ENVI Deep Learning V1.0的操作教程,重点讲解了如何利用ENVI软件进行深度学习模型的训练与应用,以实现遥感图像中特定目标(如集装箱)的自动提取。教程涵盖了从数据准备、标签图像创建、模型初始化与训练,到执行分类及结果优化的完整流程,并介绍了精度评价与通过ENVI Modeler实现一键化建模的方法。系统基于TensorFlow框架,采用ENVINet5(U-Net变体)架构,支持通过点、线、面ROI或分类图生成标签数据,适用于多/高光谱影像的单一类别特征提取。; 适合人群:具备遥感图像处理基础,熟悉ENVI软件操作,从事地理信息、测绘、环境监测等相关领域的技术人员或研究人员,尤其是希望将深度学习技术应用于遥感目标识别的初学者与实践者。; 使用场景及目标:①在遥感影像中自动识别和提取特定地物目标(如车辆、建筑、道路、集装箱等);②掌握ENVI环境下深度学习模型的训练流程与关键参数设置(如Patch Size、Epochs、Class Weight等);③通过模型调优与结果反馈提升分类精度,实现高效自动化信息提取。; 阅读建议:建议结合实际遥感项目边学边练,重点关注标签数据制作、模型参数配置与结果后处理环节,充分利用ENVI Modeler进行自动化建模与参数优化,同时注意软硬件环境(特别是NVIDIA GPU)的配置要求以保障训练效率。
内容概要:本文系统阐述了企业新闻发稿在生成式引擎优化(GEO)时代下的全渠道策略与效果评估体系,涵盖当前企业传播面临的预算、资源、内容与效果评估四大挑战,并深入分析2025年新闻发稿行业五大趋势,包括AI驱动的智能化转型、精准化传播、首发内容价值提升、内容资产化及数据可视化。文章重点解析央媒、地方官媒、综合门户和自媒体四类媒体资源的特性、传播优势与发稿策略,提出基于内容适配性、时间节奏、话题设计的策略制定方法,并构建涵盖品牌价值、销售转化与GEO优化的多维评估框架。此外,结合“传声港”工具实操指南,提供AI智能投放、效果监测、自媒体管理与舆情应对的全流程解决方案,并针对科技、消费、B2B、区域品牌四大行业推出定制化发稿方案。; 适合人群:企业市场/公关负责人、品牌传播管理者、数字营销从业者及中小企业决策者,具备一定媒体传播经验并希望提升发稿效率与ROI的专业人士。; 使用场景及目标:①制定科学的新闻发稿策略,实现从“流量思维”向“价值思维”转型;②构建央媒定调、门户扩散、自媒体互动的立体化传播矩阵;③利用AI工具实现精准投放与GEO优化,提升品牌在AI搜索中的权威性与可见性;④通过数据驱动评估体系量化品牌影响力与销售转化效果。; 阅读建议:建议结合文中提供的实操清单、案例分析与工具指南进行系统学习,重点关注媒体适配性策略与GEO评估指标,在实际发稿中分阶段试点“AI+全渠道”组合策略,并定期复盘优化,以实现品牌传播的长期复利效应。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值