我正在创建一个页面,您可以在该页面上设置给定时间的一些警报,并且这些警报是可重复的(假设您希望它在每天10:00 AM响起,每天10:00 AM响起)。
该功能必须:
我尝试了许多使用RxJS对此进行建模的方法,但最终由于我的需要而导致糟糕的设计始终存在重大缺陷。这是我尝试过的原因以及失败的原因:
使用基于一个滴答主题的订阅,该主题基本上每1秒发出一次(因为精度必须为1s)。
这很糟糕,因为我存储了很多订阅,每当编辑警报时,我都必须销毁它们并从最新的数据库更改中重新创建它们(更不用说这是实时数据库,因此更新可以来自服务器而无需由当前客户制作)。
使用NgRx动作,该动作每秒钟分派一次,以触发一种检查警报响起的效果。
再次很糟糕,因为在这里我丢失了一些数据,因为找不到这种行为来存储自定义数据的正确方法,这也使“报警前的剩余时间”监控变得非常困难。
总的来说,问题是设计不好,我找不到实现这种行为的正确方法,主要是因为我想避免每秒调用一次方法,但是另一方面,我似乎找不到这样做的另一种方式,可能是因为我对此解决方案心不在mind,这就是为什么我总是在这里。
答案 0 :(得分:2)
首先,我将创建一个可以调度自身并包含其状态的Alarm类:
class Alarm {
public lastRangTime = 0;
private stopper = new Subject<void>();
get hasRang() {
return this.lastRangTime > 0;
}
get remaining() {
return this.ringTime - Date.now();
}
// add more metadata
constructor(public ringTime: number, public interval: number) {}
start(): Observable<number> {
return timer(this.ringTime - Date.now(), this.interval)
.pipe(
tap(() => {
this.lastRangTime = this.ringTime;
this.ringTime += this.interval;
}),
takeUntil(this.stopper)
)
}
stop() {
this.stopper.next();
}
}
以及一些用于容纳所有订阅的容器/服务:
class AlarmScheduler {
private queue = new Subject<Alarm>();
private subscription: Subscription = null;
schedule(ringTime: number, interval: number = DEFAULT_INTERVAL) {
const alarm = new Alarm(ringTime, interval);
this.queue.next(alarm);
return alarm;
}
initialize() {
this.subscription = this.queue
.pipe(mergeMap(alarm => alarm.start()))
.subscribe();
}
destroy() {
this.subscription.unsubscribe();
}
}
比起您可以简单地安排来自AlarmScheduler
的警报。他们将以给定的间隔重复自己。
const scheduler = new AlarmScheduler();
scheduler.initialize();
const a1 = scheduler.schedule(Date.now() + 5000);
const a2 = scheduler.schedule(Date.now() + 10000);
工作示例:https://stackblitz.com/edit/typescript-uft7up
当然,您必须确定一些细节,但是就安排警报而言,我希望上面的代码足以帮助您入门。
答案 1 :(得分:1)
创建message queue并运行worker以检查要运行的任务。
import { BehaviorSubject, timer, from} from 'rxjs';
import { withLatestFrom, map, mergeMap, filter} from 'rxjs/operators';
class Queue {
private queue = new BehaviorSubject<Task[]>([]);
add(...tasks: Task[]) {
this.queue.next([...this.queue.getValue(), ...tasks]);
}
remove(task: Task) {
this.queue.next(this.queue.getValue().filter(item => item.id !== task.id));
}
asObservable() {
return this.queue.asObservable();
}
}
interface Task {
id: number;
at?: Date;
period?: Date;
}
function isTimeForTask(task: Task){
return true;
}
function runWorker(queue$){
return timer(0, 3000).pipe(
withLatestFrom(queue$),
map(([timer, queue])=>queue),
mergeMap(queue=>from(queue)),
filter(isTimeForTask)
)
}
const queue = new Queue();
queue.add({id: 1});
runWorker(queue.asObservable())
// Handle task
.subscribe(console.log);
queue.add({id: 2});
setTimeout(()=>queue.add({id: 3}), 6000)
setTimeout(()=>queue.remove({id: 2}), 6000)