Know Scheduler
Know Scheduler
一般情况下,我们很少会用到rxjs的Scheduler,但Scheduler可以说是控制Rxjs至关重要的角色,偶尔也有可能会需要使用Scheduler来调整事件发生的时机。
What's Scheduler?
Schedule 顾明思议,单词本身有安排的意思,因此Scheduler可以想像成是负责安排的人,具体来说安排什么呢?就是安排Observable内事件该如何发生的时机点。
一个例子,请思考一下以下代码会以什么样的顺序打印?
const {of} = rxjs
console.log('start');
of(1, 2, 3)
.subscribe({
next: result => console.log(result),
complete: () => console.log('complete')
});
console.log('end');
由于of(1,2,3)事件是同步执行的,因此结果为
/*
start
1
2
3
complete
end
*/
那么有没有办法让of(1,2,3)变成异步执行呢?我们可以在of参数的最后放上一个Scheduler来安排数据处理的顺序,以下代码通过asyncScheduler来帮助我们将of(1,2,3)变成异步的代码
const { asyncScheduler, of } = 'rxjs';
console.log('start');
of(1, 2, 3, asyncScheduler)
.subscribe({
next: result => console.log(result),
complete: () => console.log('complete')
});
console.log('end');
Different kinds of Scheduler
Schduler按照执行逻辑分成以下几类:
-
null:也就是不指定scheduler,同步执行 -
queueScheduler:也是同步执行,但在执行Rxjs会将所有同步的Observable放到queue内,再依次执行,等下我们说明这和null有什么区别 -
asapScheduler:异步执行,与Promise一样的异步处理层级,也就是microtask宏任务 -
asyncScheduler:异步执行,处理方式同setIntervael,属于macrotask层级 微任务 -
animationFrameScheduler异步执行,处理方式同requestAnimationFrame,也是属于macrotask层级,常用来做动画
一段代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<div
id="block"
style="
width: 25px;
height: 25px;
position: absolute;
top: 199px;
left: 199px;
background: green;
"
></div>
<button id="null">null</button>
<button id="queue">Queue</button>
<button id="asap">Asap</button>
<button id="async">Async</button>
<button id="animationFrame">AnimationFrame</button>
<script src="https://cdn.bootcdn.net/ajax/libs/rxjs/7.3.0/rxjs.umd.min.js"></script>
<script>
const {
SchedulerLike,
queueScheduler,
asapScheduler,
asyncScheduler,
animationFrameScheduler,
fromEvent,
range,
} = rxjs;
// 动画开始重置DOM样式
const initPosition = () => {
const blockElement = document.querySelector("#block");
blockElement.style.left = "100px";
blockElement.style.top = "100px";
};
const updatePositionByScheduler = (scheduler) => {
initPosition();
setTimeout(() => {
console.log("start");
range(0, 100, scheduler).subscribe({
next: (val) => {
const blockElement = document.querySelector("#block");
blockElement.style.left = 100 + val + "px";
blockElement.style.top = 100 + val + "px";
},
complete: () => console.log("complete"),
});
console.log("end");
}, 300);
};
fromEvent(document.querySelector("#null"), "click").subscribe(() => {
updatePositionByScheduler(null);
});
fromEvent(document.querySelector("#queue"), "click").subscribe(() => {
updatePositionByScheduler(queueScheduler);
});
fromEvent(document.querySelector("#asap"), "click").subscribe(() => {
updatePositionByScheduler(asapScheduler);
});
fromEvent(document.querySelector("#async"), "click").subscribe(() => {
updatePositionByScheduler(asyncScheduler);
});
fromEvent(document.querySelector("#animationFrame"), "click").subscribe(
() => {
updatePositionByScheduler(animationFrameScheduler);
}
);
</script>
</body>
</html>

null scheduler
range本身是同步执行的,执行顺序为:
start
complete
end
queuescheduler
queuescheduler依然是同步执行,结果与使用null scheduler一样,但有差别,等下会讲
asapScheduler
asapScheduler是异步执行,执行顺序为
start
end
complete
asapScheduler会进入microtsk,而microtask阶段是不会处理DOM渲染的,从页面看到虽然坐标有更新,但会在最后直接出现在右下角
asyncScheduler
asyncScheduler异步执行,执行顺序与asapScheduler一样。不同的是asyncScheduler是使用macrotask,DOM渲染行为会发生在每次macrotask结束之间,可以看到动画很流畅了...
animationFrameScheduler
animationFrameScheduler触发的时机与页面重绘(repaint)定义的时机点一样,与我们使用的requestAnimationFrame一样,基本上16MS左右执行一次(60HZ,大部分浏览器都是这个频率),具体看显示器刷新频率.