Scheduler 基本观念

不晓得读者们还记不记得,我们在前面的文章中有提到Scheduler 是为了解决RxJS 衍生的最后一个问题,而我们现在就在揭晓这个谜底。

其实RxJS 用久了之后就会发现Observable 有一个优势是可以同时处理同步和非同步行为,但这个优势也带来了一个问题,就是我们常常会搞不清处现在的observable 执行方式是同步的还是非同步的。换句话说,我们很容易搞不清楚observable 到底什么时候开始发送元素!

举例来说,我们可能很清楚interval是非同步送出元素的,但range呢?from呢?他们可能有时候是非同步有时候是同步,这就会变得有点困扰,尤其在除错时执行顺序就非常重要。

而Scheduler 基本上就是拿来处理这个问题的!

什么是Scheduler?

Scheduler 控制一个observable 的订阅什么时候开始,以及发送元素什么时候送达,主要由以下三个元素所组成

  • Scheduler 是一个资料结构。它知道如何根据优先级或其他标准来储存并伫列任务。
  • Scheduler 是一个执行环境。它意味着任务何时何地被执行,比如像是立即执行、在回呼(callback)中执行、setTimeout 中执行、animation frame 中执行
  • Scheduler是一个虚拟时钟。它透过now()这个方法提供了时间的概念,我们可以让任务在特定的时间点被执行。

简言之Scheduler 会影响Observable 开始执行及元素送达的时机,比如下面这个例子

var observable = Rx.Observable.create(function (observer) {
    observer.next(1);
    observer.next(2);
    observer.next(3);
    observer.complete();
});

console.log('before subscribe');
observable.observeOn(Rx.Scheduler.async) // 设为 async
.subscribe({
    next: (value) => { console.log(value); },
    error: (err) => { console.log('Error: ' + err); },
    complete: () => { console.log('complete'); }
});
console.log('after subscribe');

// "before subscribe"
// "after subscribe"
// 1
// 2
// 3
// "complete"

JSBin

上面这段程式码原本是同步执行的,但我们用了observable.observeOn(Rx.Scheduler.async)原本是同步执行的就变成了非同步执行了。

有哪些Scheduler 可以用

目前RxJS 5 Scheduler 跟RxJS 4.x 以前的版本完全不同,在RxJS 5 当中有提供四个scheduler,预设为undefined 会直接以递回的方式执行

  • queue
  • asap
  • async
  • animationFrame

这四个scheduler 我们会在下面搭配程式码一一讲解

RxJS 5跟RxJS 4.x预设的Scheduler不同,所以在某些使用情境下会出现不同的结果,例如这个issue,请特别注意。

使用Scheduler

其实我们在使用各种不同的operator时,这些operator就会各自预设不同的scheduler,例如一个无限的observable就会预设为queue

scheduler,而timer相关的operator则预设为asyncscheduler。

要使用Scheduler除了前面用到的observeOn()方法外,以下这几个creation operators最后一个参数都能接收Scheduler

  • bindCallback
  • bindNodeCallback
  • combineLatest
  • concat
  • empty
  • from
  • fromPromise
  • interval
  • merge
  • of
  • range
  • throw
  • timer

例如下面这个例子

var observable = Rx.Observable.from([1,2,3,4,5], Rx.Scheduler.async);

另外还有多个operators最后一个参数可以传入Scheduler这边就不一一列出,这已参考官方的文件,最通用的方式还是observeOn()

只要是observable就可以用这个方法。

queue

queue的运作方式跟预设的立即执行很像,但是当我们使用到递回的方法时,他会伫列这些行为而非直接执行,一个递回的operator就是他会执行另一个operator,最好的例子就是repeat(),如果我们不给他参数的话,他会执行无限多次,像下面这个例子

Rx.Observable.of(10).repeat().take(1)
.subscribe(console.log);

这个例子在RxJS 4.x的版本中执行会使浏览器挂掉,因为take(1)永远不会被执行到repeat会一直重复要元素,而在RxJS 5中他预设了无限的observable为queue所以他会把repeat的next行为先伫列起来,因为前一个complete还在执行中,而这时repeat就会回传一个可退订的物件给take(1)等到repeat的next被第一次执行时就会结束,因为take(1)会直接收到值。

使用情境:

queue 很适合用在会有递回的operator 且具有大量资料时使用,在这个情况下queue 能避免不必要的效能损耗。

asap

asap 的行为很好理解,它是非同步的执行,在浏览器其实就是setTimeout 设为0 秒(在NodeJS 中是用process.nextTick),因为行为很好理解这里就不写例子了。

使用情境:

asap 因为都是在setTimeout 中执行,所以不会有block event loop 的问题,很适合用在永远不会退订的observable,例如在背景下持续监听server 送来的通知。

async

这个是在RxJS 5 中新出现的Scheduler,它跟asap 很像但是使用setInterval 来运作,通常是跟时间相关的operator 才会用到。

animationFrame

这个相信大家应该都知道,他是利用Window.requestAnimationFrame这个API去实作的,所以执行周期就跟Window.requestAnimationFrame一模一样。

使用情境:

在做复杂运算,且高频率触发的UI 动画时,就很适合使用animationFrame,以可以搭配throttle operator 使用。

今日小结

这篇文章简单的介绍了RxJS 的Scheduler,因为篇幅的关系没有办法很细的去讲,但实务上Scheduler 的使用非常简单,只要在operator 的最后一个参数加上去或是用observeOn 就可以了。平常其实不太需要用到Scheduler,尤其在RxJS 5 中已经有针对各种情况给不同的预设,笔者最常用到的还是animationFrame!

不知道今天读者们有没有收获呢?如果有任何疑问,欢迎在下方留言给我,谢谢。

results matching ""

    No results matching ""