领先的免费Web技术教程,涵盖HTML到ASP.NET

网站首页 > 知识剖析 正文

RxJS——自制 switchMap 操作符 switch 自制软件

nixiaole 2024-11-14 18:37:43 知识剖析 24 ℃


switchMap 操作符通过名字便知和 mergeMap 很像,是用来处理嵌套流的。而它们之间的不同之处在于,switchMap 会先查看是否有“内部”订阅,如果有,switchMap 会先取消这个“内部”订阅,再对接收到的值做处理。

文字描述可能还不让你理解,我们再用代码来演示下它们的不同。

fromEvent(document, "click").pipe(
  scan(i => i + 1, 0),
  switchMap(value => of(value).pipe(delay(500))) // 在这行代码分别使用 mergeMap 和 switchMap
)
.subscribe(value => {
  console.log(value);
});

首先使用 mergeMap,在页面上连续点击 5 次,控制台将输出 1 2 3 4 5。也就是说每次点击产生的值都会被 mergeMap 处理。再来看看 switchMap,在页面上连续点击 5 次后,我们发现控制台只输出了一次,值为 5。这就是我们在文章开头说的,switchMap 每接收到新值时会检查之前有没有发生过订阅,如果有,先取消,再处理新值。由于示例代码中有 500 毫秒的延迟,只要比我们点击页面的速度慢,导致 2 3 4 5 次点击都会先取消上一次点击事件流下来的事件,再处理本次事件,由于第五次点击后面没有事件了,所以输出了 5。这就是 switchMap 的工作机理。

了解了 switchMap 的工作方式,我们就可以来写代码实现它了。首先拷贝之前写好的 MyMergeMap 代码:

class MySwitchMapSubscriber extends Subscriber {
  constructor(sub, fn) {
    super(sub);
    this.fn = fn;
  }

  _next(value) {
    const innerObservable$ = this.fn(value);

    innerObservable$.subscribe(value => {
      this.destination.next(value);
    });
  }
}

const mySwitchMap = fn => source => {
  return source.lift({
    call(sub, source) {
      source.subscribe(new MySwitchMapSubscriber(sub, fn));
    }
  });
};

下面我们来改造代码。根据我们了解到的 switchMap 的工作机理,首先我们要有内部订阅对象,这是由 innerObservable$.subscribe 这段代码产生的。然后我们在这段代码执行前对这个内部订阅对象进行检查,如果有值,说明有过订阅,就 unsubscribe 它。修改后代码如下:

class MySwitchMapSubscriber extends Subscriber {
  constructor(sub, fn) {
    super(sub);
    this.fn = fn;
    this.innerSubscription = null;
  }

  _next(value) {
    const innerObservable$ = this.fn(value);

    if (this.innerSubscription) {
      this.innerSubscription.unsubscribe();
    }

    this.innerSubscription = innerObservable$.subscribe(value => {
      this.destination.next(value);
    });
  }
}

const mySwitchMap = fn => source => {
  return source.lift({
    call(sub, source) {
      source.subscribe(new MySwitchMapSubscriber(sub, fn));
    }
  });
};

我们在 MySwitchMapSubscriber 自定义类的构造函数中,创建了成员变量 innerSubscription,用来保存内部订阅对象的值。然后我们在订阅内部对象前,加入了代码检查这个成员变量是否有值,有值就 unsubscribe 它。这样自制版的 switchMap 操作符就完成了。是不是很简单?它和 mergeMap 操作符的区别可以说只有一行代码之差。假设示例代码的每次点击都和一个 http 请求关联,这种场景该用 mergeMap 还是 switchMap 呢?留给大家去思考吧。

如有任何问题,请添加微信公众号“读一读我”。

Tags:

最近发表
标签列表