Time Bomb Veela
  1. 1 Time Bomb Veela
  2. 2 かかってこいよ NakamuraEmi
  3. 3 Libertus Chen-U
  4. 4 Flower Of Life 发热巫女
  5. 5 Hypocrite Nush
  6. 6 One Last You Jen Bird
  7. 7 The Night We Stood Lyn
  8. 8 Life Will Change Lyn
  9. 9 Warcry mpi
  10. 10 Quiet Storm Lyn
  11. 11 Last Surprise Lyn
2018-04-14 22:06:59

RxJS核心概念解析——Scheduler

RxJS的订阅行为是同步的,但数据的流动逻辑既可以同步也可以异步。这样就造成了一个问题,当数据的流动和组合逻辑变得复杂时,尤其是同步和异步数据混合后,整体状态会变得难以控制。为了解决这个问题,Scheduler则应运而生。

JavaScript中的异步

在讨论不同类型的Schedulers之前,我们有必要先回顾一下JS中的异步任务的工作原理。看下面这个例子:

console.log('start');

setTimeout(() => {
    console.log('setTimeout');
}, 0);

Promise.resolve().then(() => {
    console.log('promise1');
}).then(() => {
  console.log('promise2');
});

console.log('end');

// 执行结果
// start
// end 
// promise1
// promise2
// setTimeout

这里只简单分析一下,start和end为同步执行,setTimeout和promise为异步执行置于事件循环队列,两者先后区别在于promise为microtask队列,setTimeout为marcotask队列,在当前调用栈清空后,会先执行mircotask队列再去执行新一轮的marcotask队列。更详细的内容请查阅eventloop相关资料。大概知道不同异步执行时机之后,就可以看看RxJS中的Scheculer了。

Scheduler分类

null

先看一个例子:

const a$ = Rx.Observable.from([1, 2]);
const b$ = Rx.Observable.of(3);

const c$ = Rx.Observable.combineLatest(a$, b$, (a, b) => a + b);

c$.subscribe(c => console.log(c));

// 5

可以看到在这个例子中,a和b数据流同时被c订阅,数据1,2,3的流出顺序是不能确定的。在这种数据流出顺序不确定的情况下,RxJS必须有一套相对应的调度规则来处理,分别于调度器相对应。

默认调度器为null,即采用同步递归的方式流出数据。以上实例的流出顺序如下:

  1. c$被订阅
  2. 按传参顺序订阅a$,数据同步流出
  3. a$流出1,c$保存
  4. a$流出2,c$保存
  5. a$完成, 第二个参数b$被订阅
  6. b$流出3,c$保存,触发combineLatest输出5

asap

该类调度器管理microtask中的数据流动,可以看做是最快速的异步操作。通过该调度器改写上例流出顺序如下:

const a$ = Rx.Observable.from([1, 2], Rx.Scheduler.asap);
const b$ = Rx.Observable.of(3);

const c$ = Rx.Observable.combineLatest(a$, b$, (a, b) => a + b);

c$.subscribe(c => console.log(c));

// 4
// 5

我们将a$的数据流动置于mircotask中,导致的直接结果为b$先流出3被c$保存,之后流出的1,2都能触发combineLatest输出4,5。

async

该类调度器即管理macrotask中的数据流动。使用方式同上。

animationFrame

该类调度器对接于Window.requestAnimationFrame的调用周期,通常搭配于高频率触发的UI动画结合数据流使用。

queue

该类调度器多用于数据流的迭代操作控制,属于同步调度,对于2次以上的迭代流出会将其置于queue中等待当前同步任务完成后再执行。例子如下:

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

// 10

repeat操作符默认为queue调度,如果为递归调度则会无限重复repeat不会执行到take操作符,而在此repeat只会流出1次数据被take接收后complete数据流,队列中的数据流动也就不会执行了。

Scheduler使用

其实即使我们不知道调度器,我们也已经在使用操作符的过程中使用过调度器了。因为每个操作符都会根据最小并发原则默认选择一个合适的调度器。例如大多数操作符默认选择null调度器。对于有可能返回大量或无限数据的操作符,默认选择queue调度器。对于不同的异步操作符,默认选择async或者asap调度器。

如果我们需要手动决定不同操作符的调度器,可通过如下方法

  • 静态创建操作符通常可以接受调度器参数,实例操作符也有部分可以接受调度器参数。
  • 每个Observable都可以使用observeOn方法来调度其流出数据的上下文。
  • 每个Observer都可以通过subscribeOn方法来决定订阅者响应数据的上下文。

-- EOF --

添加在分类「 前端开发 」下,并被添加 「RxJS」 标签。