头图

isStable API 源代码如下:

return this.activeCartId$.pipe(
      switchMap((cartId) => this.multiCartService.isStable(cartId)),
      debounce((state) => (state ? timer(0) : EMPTY)),
      distinctUntilChanged()
    );

EMPTY 操作符:直接发送 complete 事件。

看一个简单的例子:

import { EMPTY } from 'rxjs';

EMPTY.subscribe({
  next: () => console.log('Next'),
  complete: () => console.log('Complete!'),
});

// Outputs
// Complete!

console.log('fire');
EMPTY.subscribe((value) => console.log('value: ', value));

我们永远不会在 console 里看到 Next 或者 value 的打印,因为 EMPTY 不 emit 任何数据,只有 complete 的 event.

这里引入 debounce 是为了避免 flicker spinner.
当数据加载非常快时,如果用户看到只有几分之一秒的 spinner 动画一闪而过,这是很不好的用户体验。

避免这个用户交互问题的方式之一,是引入 非闪烁加载器(Non-flicker loader).

这个 loader 需要满足的需求:

  1. 如果加载时间少于 1 秒——我们根本不会显示 loader;
  2. 如果加载时间超过一秒——我们开始显示加载器并在屏幕上保持至少 1 秒。

例如,我们的数据加载需要 1.2 秒。 第1个 1 秒钟我们什么都不显示,1秒钟过后出现一个加载器。 又过了 0.2 秒后,我们收到数据,但我们将 spinner 在屏幕上保持 0.8 秒,此这个 spinner 不会出现闪烁现象。

实际代码:

readonly load$ = new Subject<void>();

readonly cancel$ = new Subject<void>();

readonly data$ = this.load$.pipe(
  switchMapTo(nonFlickerLoader(this.dataService.load())),
  map(value => value ?? 'Loading...'),
  startWith('No data'),
  takeUntil(this.cancel$),
  repeat(),
);

这里我们有两个 triggering subject 来开始和取消加载。 假设我们的 nonFlickerLoader 在应该指示加载时发出 null,而在我们应该显示它的时间点,发出实际被加载的数据。 我们使用 repeat 操作符,所以我们可以多次触发它。

现在让我们继续编写 nonFlickerLoader 函数。 首先我们定义接口。 我们使用通用 <T> 因为我们不知道我们将获得的响应类型:

function nonFlickerLoader<T>(
  data$: Observable<T>,
  delay: number = 1000,
  duration: number = 1000
): Observable<T | null> {
  // ...
}

这个函数接受数据 Observable 和两个可选参数来表示我们在显示加载指示之前等待多长时间以及应该显示该 spinner 的最小持续时间。

函数具体的实现代码,首先创建一个 loading Observable:

const loading$ = timer(delay, duration).pipe(
  map(i => !i),
  takeWhile(Boolean, true),
  startWith(false),
)

这个 $loading 是一个标志位,告诉我们 spinner 是否应该显示。

  • map 将除了第一个 emit 值 false 之外的所有值全部转换成 false.
  • takeWhile 只允许第一个 emit 值通过。

map 将除第一个之外的所有发射设置为 false。 takeWhile 用于仅在延迟时间到期后和持续时间过去后的第二次发射时让第一次发射通过。 请注意,我们使用第二个参数让布尔条件失败的假值在完成流之前通过。 我们还 startWith(false) 因为我们只想在给定延迟后显示加载指示。

注意 timer 的第二个参数:

// RxJS v6+
import { timer } from 'rxjs';

/*
  timer takes a second argument, how often to emit subsequent values
  in this case we will emit first value after 1 second and subsequent
  values every 2 seconds after
*/
const source = timer(1000, 2000);
//output: 0,1,2,3,4,5......
const subscribe = source.subscribe(val => console.log(val));

下面的代码含义是,1秒之后发射整数1,然后每隔 2 秒发射一个递增的整数序列。


注销
1k 声望1.6k 粉丝

invalid