JavaScript 中的可观察对象 (Observables)2025年3月2日 | 阅读 8 分钟 Observable 是一种基于推送的异步数据流处理技术。它主要通过 JavaScript 的响应式扩展(RxJS)引入。与处理单个事件的 Promise 不同,Observable 在处理复杂系统、多个异步活动或实时更新时更加灵活和有益。 Observable 的基础Observable 本质上是实现观察者模式的一种方式。观察者模式是一种流行的设计模式,其中一个主体(主题)维护其依赖项(观察者)的列表,并在其状态发生任何变化时通知它们。随着时间的推移,Observable 会发出数据,而观察者会订阅这些数据,并在每次发出新值时做出响应。 Observable: 这是数据的来源或生产者。它会生成可以被其他实体观察到的信息(或事件)。Observable 可以发送三种不同类型的通知:
Observer 是一个消费或订阅 Observable 以响应所发出数据的实体。通常,Observer 使用三种方法: 每次 Observable 发出值时调用(next(value))。 Error(err): 发生错误时调用。 Complete(): 当 Observable 的序列完成时调用。 Subscription: 订阅 Observable 后,Observer 会收到一个 Subscription 对象。这个对象代表了 Observable 和 Observer 之间的活动关系。此外,它还提供了一个方法(unsubscribe)来停止接收数据,确保不再发送。 Operators: Operators 是用于组合和操作 Observable 的方式。它们是纯函数,可以用来合并、过滤或转换数据流。它们接收一个 Observable 作为输入,并输出一个新的 Observable。 Schedulers: RxJS 提供了 Schedulers 来控制数据何时以及如何发出。它们在性能优化方面非常有用,特别是在处理复杂的异步操作或大数据流时。 创建: 第一步是创建 Observable,这可以通过手动创建或通过 工厂函数(如 of()、from()、interval() 或 ajax()(在 RxJS 中))来完成。这些方法可以从各种来源(如 Promise、数组甚至用户定义的事件)创建 Observable 序列。 Subscriber: Subscriber 是一个跟踪 Observable 的实体。订阅后,Observer 开始接收 Observable 发出的值。当 Observable(如事件流)发出多个值时,Observer 都会收到通知。 执行: Observable 可能会发出零个、一个或多个值。这些值会触发 Observer 的响应,并可以根据需要进行转换或处理。 错误处理和完成: Observable 要么抛出错误,要么完成值的发出。一旦完成,Observer 就不会再收到数据。如果出现问题,Observer 可以优雅地处理它并停止进一步处理。 取消订阅: 如果 Observer 认为不再需要监听 Observable(例如,由于性能问题或应用程序状态的变化),它可以选择停止接收数据。
为什么我们使用 Observables?1. 处理随时间变化的多值Observable 的一个基本优势在于其处理随时间变化的多值的能力。然而,JavaScript 的 Promise 旨在只接受或拒绝一个值或一个错误。一旦返回该值或错误,就不会有更多更新,Promise 就完成了。相反,Observable 在完成之前可以持续发出值流。 用户输入事件: 例如,跟踪击键或鼠标移动的 Observable 在用户与界面交互时可以持续发出数据(字符或坐标)。 WebSocket 连接: 在实时消息应用程序中,只要连接保持活动状态,Observable 就可以持续发出从服务器接收到的消息。 2. 惰性执行效率: 只有在订阅 Observable 时才生成值,从而节省资源,避免不必要的计算或 API 调用。 延迟执行: Observable 可以配置为定义一个方法,但其实际执行可能会延迟,直到用户明确请求它。 输出 Before subscription Observable executed Hello World After subscription 3. 集成的取消机制取消订阅的能力是 Observable 相较于 Promise 的一项关键优势。通常,您可能希望在 Observable 完成之前暂停它,特别是在耗时操作中,或者当最终结果不再有用时。通过提供取消订阅的选项,可以防止不必要的操作和内存泄漏,从而保护内存。 例如,在单页应用程序 (SPA) 中,如果用户在调用结束前离开了页面,您可以取消订阅 Observable 以停止 API 请求并防止任何进一步的处理。Promise 没有此功能;它们会持续执行,直到被解决或拒绝。 4. 声明式结构和链接Observable 提供了强大的操作符,可以帮助您以声明式方式转换、组合、过滤或处理数据流。这是使用 Observable 的最有力论据之一,尤其是在具有多个异步数据流的复杂应用程序中。 map(): 转换发出的值。 filter(): 对发出的值应用条件。 merge(): 通过组合多个 Observable 来创建一个单一的 Observable。 debounceTime(): 延迟 Observable 的发出预设的时间量。 这些操作符可以轻松地组合 Observable 来创建复杂的数据流。例如,您可以使用 debounceTime() 操作符来处理用户输入,只在用户停止输入 500 毫秒后才执行——这对于搜索输入框来说是一个典型的用例。 5. 错误处理和重试机制Observable 提供了特殊的错误处理策略。通过 error() 方法,Observer 可以处理在 Observable 生命周期的任何点发生的错误。相比之下,Promise 仅限于一次性处理失败,无论是通过拒绝 Promise 还是使用 .catch()。这意味着 Observable 为错误处理提供了更多的控制。 此外,Observable 可以使用 RxJS 提供的 retry() 等操作符自动重试失败的操作给定的次数。这对于网络请求特别有用,因为连接问题可能会导致临时故障。 6. 并发管理您可以使用 Observable 轻松管理异步操作的并发和时序。switchMap()、mergeMap() 和 concatMap() 等操作符允许开发人员以不同的方式管理事件的并发流。 例如,如果一个新请求在旧请求仍在处理时发出,switchMap() 会取消前一个请求并只处理最新的一个。这在处理搜索建议等场景时非常有用,因为您只需要显示最新查询的结果。 挑战1. 陡峭的学习曲线最大的障碍之一是 Observable 陡峭的学习曲线,特别是对于不熟悉观察者模式或响应式编程的开发人员。熟悉命令式编程范例的开发人员可能对响应式编程引入的流、惰性执行和函数式操作符(如 map()、filter()、switchMap() 等)等概念感到不适应。 操作符的复杂性: RxJS 提供了大量的 操作符 来处理数据流,但掌握它们的高效使用可能很困难。某些操作符,如 switchMap()、mergeMap() 和 concatMap(),具有细微的差异,需要实践才能理解和应用每一个。 2. 内存管理和泄漏的开销内存管理是使用 Observable 时的另一个挑战。未能取消订阅 Observable 可能导致内存泄漏,因为 Observable 是惰性执行的,并且有潜力随着时间的推移发出值。 在长时间运行的系统中,取消订阅 Observable 对于释放资源至关重要。在使用 Angular 等框架时,您通常需要在生命周期钩子(如 ngOnDestroy())中显式取消订阅 Observable。 内存泄漏: 如果 Observer 继续监听 Observable,它们的内存可能会被消耗。 不必要的开销: 如果 Observable 继续发出值,它可能会进行无用的网络请求或事件处理,从而减慢应用程序的速度。 3. 调试和跟踪困难操作符链: 当链中有多个操作符时,很难理解数据是如何被转换的,或者每个步骤在做什么。例如,使用 switchMap() 而不是 mergeMap() 可能会导致问题,但在大型代码库中查找和解决此类错误可能很困难。 错误处理: 必须使用订阅中的 error() 回调 来捕获 Observable 可能遇到的难以处理的错误。如果错误未被正确处理,它们可能会悄悄地扩散到整个应用程序,使得很难确定其根本原因。 4. 大型应用程序中的性能问题高频事件处理: 对于处理鼠标移动等事件流的 Observable,对活动进行节流或防抖至关重要。可以使用 throttleTime() 和 debounceTime() 等操作符来防止系统过于频繁地发出值,并使应用程序过载不必要的更新。 多次订阅: 多次订阅同一个 Observable 可能会产生重复的工作。每个订阅者都可能触发 Observable 的一次独立执行,这可能导致多次网络请求、事件处理程序或计算。 5. 操作符过载和滥用过多的操作符可能会影响整体性能并使 Observable 链复杂化。例如,在同一操作中使用 throttleTime() 和 debounceTime() 可能会导致意外行为。 不正确的操作符: 当误用 switchMap()、mergeMap() 和 concatMap() 等操作符时,可能会产生意外的结果。使用错误的操作符可能导致数据丢失或重复的网络请求,因为每个操作符处理并发的方式都不同。 6. 兼容性和框架锁定RxJS Observable 在 Angular 框架 中被广泛使用,但在 React 或 Vue 等其他 JavaScript 环境中则不那么常见。因此,如果开发人员开始依赖 RxJS Observable,他们可能会被限制使用特定的框架或库。 下一主题JavaScript 中比较数组 |
我们请求您订阅我们的新闻通讯以获取最新更新。