RxJS 中的调度器(Scheduler)是什么?如何使用?
调度器是什么
调度器决定 RxJS 中通知(next/error/complete)的执行时机和上下文。简单说:它控制一段 Observable 逻辑"在什么时候跑"——同步、微任务、宏任务还是动画帧。
RxJS 内置 5 种调度器,核心区别如下:
| 调度器 | 底层机制 | 典型场景 |
|---|---|---|
| null(同步) | 直接递归调用 | 默认,少量数据 |
| queueScheduler | 同步蹦床调度 | 递归/迭代,防栈溢出 |
| asapScheduler | Promise.then(微任务) | 优先级高于宏任务的异步 |
| asyncScheduler | setInterval(宏任务) | 延迟、定时操作 |
| animationFrameScheduler | requestAnimationFrame | 浏览器动画 |
怎么指定调度器
三种方式:
1. 创建时传入——作为操作符最后一个参数:
typescriptof(1, 2, 3, asyncScheduler).subscribe(console.log);
2. observeOn / subscribeOn:
observeOn(asyncScheduler):下游通知切到异步调度subscribeOn(asyncScheduler):上游订阅动作切到异步调度
二者区别常被追问:subscribeOn 影响的是"什么时候开始执行 Observable 逻辑",observeOn 影响的是"下游在哪个调度器上接收通知"。
3. schedule 方法——手动调度:
typescriptasyncScheduler.schedule(() => console.log('1s后'), 1000);
面试高频追问
asapScheduler 和 asyncScheduler 有什么区别?
asapScheduler 走微任务队列(Promise.then),asyncScheduler 走宏任务队列(setTimeout/setInterval)。微任务优先级更高,会在当前宏任务结束后、下一个宏任务之前执行。需要尽快响应但不阻塞主线程时选 asap,需要真正延迟时选 async。
queueScheduler 和同步调度器有什么区别?
同步调度器直接递归调用,大量数据可能栈溢出。queueScheduler 用蹦床调度(trampoline),把递归展开为循环迭代,避免栈溢出同时保持同步语义。
什么时候该手动指定调度器?
大多数时候不需要——RxJS 按最小并发原则自动选择默认调度器。手动指定主要出现在三个场景:测试中用 TestScheduler 控制时间、动画中用 animationFrameScheduler 同步渲染帧、需要改变默认执行上下文时。
如何在测试中控制调度器?
使用 TestScheduler,它提供虚拟时钟,可以用 marble diagram 声明时序并同步断言结果,避免真实等待。
选择建议
不需要调度器就不加。需要延迟选 async,需要非阻塞优先级选 asap,递归防溢出选 queue,动画选 animationFrame。过度使用调度器只会增加复杂度和性能开销。