rxjs原理解析

一. rxjs介绍

rxjs是一个函数式编程的工具库,即推崇纯函数的开发理念。纯函数的概念这里不过多展开,读者可以自行查阅。简单理解就是不依赖this,稳定的输入输出。

代码示例如下

javascript 复制代码
import { Observable, map } from 'rxjs'

const observable = new Observable(subscriber => {
  subscriber.next(1)
}).pipe(map(x => x * 2))

observable.subscribe(x => {
  console.log(x)
})

二. rxjs原理

rxjs核心原理是实现了一套类似EventEmitter的事件订阅/派发更新的机制,且结合懒执行/惰性执行的机制优化性能。在下文实现fromEvent这个API时会详细说明。

rxjs有两个核心对象,一个是Observable,用于收集事件监听方法,另一个是Subscriber,用于收集事件派发方法。

2.1 定义Observable对象原型

Observable对象核心属性_subscribe记录事件监听方法,核心方法subscribe创建Subscriber对象实例,收集事件派发方法,执行事件监听处理逻辑。

javascript 复制代码
function Observable(subscribe) {
  if (subscribe) this._subscribe = subscribe
}

Observable.prototype.subscribe = function (observerOrNext, error, complete) {
  const subscriber = new Subscriber(observerOrNext, error, complete)
  const operator = this.operator
  const source = this.source
  subscriber.add(
    operator
      ? operator.call(subscriber, source)
      : this._trySubscribe(subscriber),
  )
}

Observable.prototype._trySubscribe = function (subscriber) {
  try {
    return this._subscribe(subscriber)
  } catch (err) {
    subscriber.error(err)
  }
}

2.2 定义Subscriber对象原型

Subscriber对象的partialObserver属性用于收集派发更新方法,是一个对象,核心属性有nexterrorcomplete,其中next属性是必传的。

EventEmitter类似,提供了类似off方法的逻辑解除事件监听逻辑,主要通过addunsubscribe方法,add方法用于收集解除事件监听方法,unsubscribe方法用于执行解除事件监听方法。

javascript 复制代码
function Subscriber(observerOrNext, error, complete) {
  // 是否终止执行next、error、complete事件
  this.isStopped = false
  this.closed = false
  this._finalizers = []

  if (isFunction(observerOrNext)) {
    this.partialObserver = {
      next: observerOrNext,
      error,
      complete,
    }
  } else {
    this.partialObserver = observerOrNext
  }
}

Subscriber.prototype.next = function (value) {
  if (this.isStopped) return
  if (this.partialObserver.next) this.partialObserver.next(value)
}

Subscriber.prototype.error = function (err) {
  if (this.isStopped) return
  if (this.partialObserver.error) this.partialObserver.error(err)
}

Subscriber.prototype.complete = function () {
  if (this.isStopped) return
  this.isStopped = true
  if (this.partialObserver.complete) this.partialObserver.complete()
}

Subscriber.prototype.add = function (finalizer) {
  if (finalizer) {
    if (this.closed) {
      finalizer()
    } else {
      this._finalizers.push(finalizer)
    }
  }
}

Subscriber.prototype.unsubscribe = function () {
  if (this.closed) return
  this.closed = true
  this.isStopped = true
  this._finalizers.forEach(cb => cb())
}

2.3 实现fromEvent

fromEvent方法作用是添加DOM事件监听。代码逻辑比较简单,核心是学习懒执行的思想。

javascript 复制代码
function isEventTarget(target) {
  return (
    isFunction(target.addEventListener) &&
    isFunction(target.removeEventListener)
  )
}

function isNodeStyleEventEmitter(target) {
  return isFunction(target.addListener) && isFunction(target.removeListener)
}

const eventTargetMethods = ['addEventListener', 'removeEventListener']
const nodeEventEmitterMethods = ['addListener', 'removeListener']

export function fromEvent(target, eventName, options) {
  const listeners = (
    isEventTarget(target)
      ? eventTargetMethods
      : isNodeStyleEventEmitter(target)
        ? nodeEventEmitterMethods
        : []
  ).map(
    method =>
      function (handler) {
        target[method](eventName, handler, options)
      },
  )
  if (!listeners.length) throw new TypeError('Invalid event target')
  const addEventListener = listeners[0]
  const removeEventListener = listeners[1]
  
  return new Observable(subscriber => {
    const handler = function (...args) {
      return subscriber.next(...args)
    }
    addEventListener(handler)
    return () => removeEventListener(handler)
  })
}

结合下面代码示例,解析整体执行流程:

首先调用fromEvent方法时,会创建Observable对象实例,收集添加DOM事件监听方法,需要注意的是此时还没有执行事件绑定逻辑的,当调用observable.subscribe方法时,才执行DOM事件监听逻辑,同时收集事件回调方法。假如这时触发click事件,会执行handler方法,调用subscriber.next方法执行事件回调。

这里懒执行的思想主要体现在DOM事件监听的执行时机不是一开始就执行,而是当添加订阅事件回调时才执行DOM事件监听逻辑。

javascript 复制代码
import { fromEvent } from 'rxjs'

const observable = fromEvent(document, 'click')

observable.subscribe(evt => {
  console.log(evt)
})

2.4 实现函数式编程

rxjs提供了丰富的API,可以通过pipe方法进行API的组装,举个🌰,类似工厂流水线,流水线中包含多道工序,可以根据业务场景自由增删工序。

例如下面这段代码,数据会先经过filter处理,符合条件的数据会流转map处理,最终流转到业务事件回调。

javascript 复制代码
import { filter, map, Observable } from 'rxjs'

const observable = new Observable(subscriber => {
  subscriber.next(1)
}).pipe(
  filter(x => x),
  map(x => x * 2),
)

observable.subscribe(x => {
  console.log(x)
})

2.4.1 实现pipe方法

pipe方法入参operations是数组类型,元素值是API方法,会循环遍历数组元素,创建其对应的Observable对象实例,observable.source指向上一个Observable对象实例,observable.operator指向API的执行方法。

javascript 复制代码
Observable.prototype.lift = function (operator) {
  const observable = new Observable()
  observable.source = this
  observable.operator = operator
  return observable
}

Observable.prototype.pipe = function (...operations) {
  return (function piped(input) {
    return operations.reduce((prev, operation) => operation(prev), input)
  })(this)
}

2.4.2 实现mapfilter

rxjsAPI很多,这里只实现mapfilter,了解其核心原理即可。每个API都对应一个Observable对象实例,会调用其subscribe方法添加事件派发更新回调,当触发顶层Observable对象的_subscribe方法时,则会依次调用API代码逻辑。

javascript 复制代码
function operate(init) {
  return function (source) {
    return source.lift(function (liftedSource) {
      try {
        return init(liftedSource, this)
      } catch (err) {
        this.error(err)
      }
    })
  }
}

function filter(cb, thisArg) {
  return operate(function (source, subscriber) {
    source.subscribe(function (value) {
      if (cb.call(thisArg, value)) subscriber.next(value)
    })
  })
}

function map(cb, thisArg) {
  return operate(function (source, subscriber) {
    source.subscribe(function (value) {
      subscriber.next(cb.call(thisArg, value))
    })
  })
}

三. 总结

rxjs是一个优秀的函数式编程框架,如果业务中有使用函数式编程的诉求,那可以考虑使用rxjs。最重要的是学习rxjs的核心理念,即事件订阅/派发更新的设计模式,配合懒执行机制优化性能。另外就是函数式编程的理念。代码仓库

创作不易,如果文章对你有帮助,那点个小小的赞吧,你的支持是我持续更新的动力!

相关推荐
崔庆才丨静觅4 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60615 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了5 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅5 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅5 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅6 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment6 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅6 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊6 小时前
jwt介绍
前端
爱敲代码的小鱼6 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax