了解rxjs中的defer

SegmentFault

共 4067字,需浏览 9分钟

 ·

2021-10-14 01:49

作者:阿古达木

来源:SegmentFault 思否社区


下面介绍一个少有人知道的observable -- defer,如何使用,什么时候使用
读这篇文章之前,你需要对rxjs基础用法有一定的了解


假设我们需要写一个自定义operator叫做tapOnce。接收一个函数当作参数,只有流的第一次触发时才执行

function tapOnce(fn: Function) {
  let run = false;
  return function (source: Observable<any>) {
    return source.pipe(
      tap((data) => {
        if (!run) {
          fn(data);
          run = true;
        }
      })
    );
  };
}


这段代码简单直观,在tap的基础上,用了一个变量来控制执行次数,调用一下

const test$ = interval(1000).pipe(tapOnce((d) => console.log('🐶', d)));

test$.subscribe();
// 1s之后打印 🐶0


运行很正常,在流第一次触发的时候打印狗头。

要是再加一个订阅者呢?

const test$ = interval(1000).pipe(tapOnce((d) => console.log('🐶', d)));

test$.subscribe();
test$.subscribe();
// 1s之后打印 🐶0


结果只打印了一遍,这是因为两个订阅者订阅同一个流,使用同一个run变量。

想要打印两遍,我们就需要一个能够在订阅时才创建流的功能。
defer就是用来做这件事的
改进一下

function tapOnce(fn: Function) {
  return function (source: Observable<any>) {
    return defer(() => {
      let run = false;
      return source.pipe(
        tap((data) => {
          if (!run) {
            fn(data);
            run = true;
          }
        })
      );
    });
  };
}


defer接收一个返回类型为observable的函数。只有当defer被订阅了,函数才会执行。而不是在创建时。然后利用js闭包,让每个订阅者有自己的作用域。

通过简单的抽象类看一下defer到底是怎么实现的

function defer(observableFactory: () => ObservableInput<any>) {
  return new Observable(subscriber => {
    const source = observableFactory();
    return source.subscribe(subscriber);
  });
}


defer返回一个新的observable。当有订阅者订阅的时候,就会执行工厂方法,创建并返回新的observalbe。

看看defer还能在什么场景下发挥作用,假设有一个这样的需求,每次订阅的时候返回一个随机数

const randNum = of(Math.random());
 
randNum.subscribe(console.log);
randNum.subscribe(console.log);


这里每一个订阅者打印的值是一样的,我们可以用defer改进一下

const randNum = defer(() => of(Math.random()));

randNum.subscribe(console.log);
randNum.subscribe(console.log);

// 等同于这种写法
const randNum2 = () => of(Math.random());
randNum2().subscribe(console.log);
randNum2().subscribe(console.log);


另一种场景是我们想要延迟一下promise的执行时间。当有订阅者的时候,promise才执行。实现一个lazyPromise

// 此时console.log('promise')已经执行
const promise = new Promise((resolve) => {
  console.log('promise');
  setTimeout(() => resolve('promise'), 1000);
});

// console.log('lazy promise');只有当被订阅才执行
const lazyPromise = defer(() => {
  return new Promise((resolve) => {
    console.log('lazy promise');
    setTimeout(() => resolve('promise'), 1000);
  });
});

lazyPromise.subscribe(console.log);


Promises天生就是热流,无视订阅者。我们可以通过defer,把promise变成一个Observable-like

代码:https://stackblitz.com/edit/rxjs-zw5ujo?file=index.ts



点击左下角阅读原文,到 SegmentFault 思否社区 和文章作者展开更多互动和交流,扫描下方”二维码“或在“公众号后台回复“ 入群 ”即可加入我们的技术交流群,收获更多的技术文章~


- END -


浏览 15
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报