@most/scheduler
Version:
Reactive programming with lean, functions-only, curried, tree-shakeable API
441 lines (427 loc) • 15.2 kB
JavaScript
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@most/prelude')) :
typeof define === 'function' && define.amd ? define(['exports', '@most/prelude'], factory) :
(global = global || self, factory(global.mostScheduler = {}, global.mostPrelude));
}(this, (function (exports, prelude) { 'use strict';
var ScheduledTaskImpl = /** @class */ (function () {
function ScheduledTaskImpl(time, localOffset, period, task, scheduler) {
this.time = time;
this.localOffset = localOffset;
this.period = period;
this.task = task;
this.scheduler = scheduler;
this.active = true;
}
ScheduledTaskImpl.prototype.run = function () {
return this.task.run(this.time - this.localOffset);
};
ScheduledTaskImpl.prototype.error = function (e) {
return this.task.error(this.time - this.localOffset, e);
};
ScheduledTaskImpl.prototype.dispose = function () {
this.active = false;
this.scheduler.cancel(this);
return this.task.dispose();
};
return ScheduledTaskImpl;
}());
var RelativeScheduler = /** @class */ (function () {
function RelativeScheduler(origin, scheduler) {
this.origin = origin;
this.scheduler = scheduler;
}
RelativeScheduler.prototype.currentTime = function () {
return this.scheduler.currentTime() - this.origin;
};
RelativeScheduler.prototype.scheduleTask = function (localOffset, delay, period, task) {
return this.scheduler.scheduleTask(localOffset + this.origin, delay, period, task);
};
RelativeScheduler.prototype.relative = function (origin) {
return new RelativeScheduler(origin + this.origin, this.scheduler);
};
RelativeScheduler.prototype.cancel = function (task) {
return this.scheduler.cancel(task);
};
RelativeScheduler.prototype.cancelAll = function (f) {
return this.scheduler.cancelAll(f);
};
return RelativeScheduler;
}());
/** @license MIT License (c) copyright 2010-2017 original author or authors */
var defer = function (task) {
return Promise.resolve(task).then(runTask);
};
function runTask(task) {
try {
return task.run();
}
catch (e) {
return task.error(e);
}
}
/** @license MIT License (c) copyright 2010-2017 original author or authors */
var SchedulerImpl = /** @class */ (function () {
function SchedulerImpl(timer, timeline) {
var _this = this;
this._runReadyTasksBound = function () { return _this._runReadyTasks(); };
this.timer = timer;
this.timeline = timeline;
this._timer = null;
this._nextArrival = Infinity;
}
SchedulerImpl.prototype.currentTime = function () {
return this.timer.now();
};
SchedulerImpl.prototype.scheduleTask = function (localOffset, delay, period, task) {
var time = this.currentTime() + Math.max(0, delay);
var st = new ScheduledTaskImpl(time, localOffset, period, task, this);
this.timeline.add(st);
this._scheduleNextRun();
return st;
};
SchedulerImpl.prototype.relative = function (offset) {
return new RelativeScheduler(offset, this);
};
SchedulerImpl.prototype.cancel = function (task) {
task.active = false;
if (this.timeline.remove(task)) {
this._reschedule();
}
};
// @deprecated
SchedulerImpl.prototype.cancelAll = function (f) {
this.timeline.removeAll(f);
this._reschedule();
};
SchedulerImpl.prototype._reschedule = function () {
if (this.timeline.isEmpty()) {
this._unschedule();
}
else {
this._scheduleNextRun();
}
};
SchedulerImpl.prototype._unschedule = function () {
this.timer.clearTimer(this._timer);
this._timer = null;
};
SchedulerImpl.prototype._scheduleNextRun = function () {
if (this.timeline.isEmpty()) {
return;
}
var nextArrival = this.timeline.nextArrival();
if (this._timer === null) {
this._scheduleNextArrival(nextArrival);
}
else if (nextArrival < this._nextArrival) {
this._unschedule();
this._scheduleNextArrival(nextArrival);
}
};
SchedulerImpl.prototype._scheduleNextArrival = function (nextArrival) {
this._nextArrival = nextArrival;
var delay = Math.max(0, nextArrival - this.currentTime());
this._timer = this.timer.setTimer(this._runReadyTasksBound, delay);
};
SchedulerImpl.prototype._runReadyTasks = function () {
this._timer = null;
this.timeline.runTasks(this.currentTime(), runTask);
this._scheduleNextRun();
};
return SchedulerImpl;
}());
/** @license MIT License (c) copyright 2010-2017 original author or authors */
var TimelineImpl = /** @class */ (function () {
function TimelineImpl() {
this.tasks = [];
}
TimelineImpl.prototype.nextArrival = function () {
return this.isEmpty() ? Infinity : this.tasks[0].time;
};
TimelineImpl.prototype.isEmpty = function () {
return this.tasks.length === 0;
};
TimelineImpl.prototype.add = function (st) {
insertByTime(st, this.tasks);
};
TimelineImpl.prototype.remove = function (st) {
var i = binarySearch(getTime(st), this.tasks);
if (i >= 0 && i < this.tasks.length) {
var events = this.tasks[i].events;
var at = prelude.findIndex(st, events);
if (at >= 0) {
events.splice(at, 1);
if (events.length === 0) {
this.tasks.splice(i, 1);
}
return true;
}
}
return false;
};
/**
* @deprecated
*/
TimelineImpl.prototype.removeAll = function (f) {
for (var i = 0; i < this.tasks.length; ++i) {
removeAllFrom(f, this.tasks[i]);
}
};
TimelineImpl.prototype.runTasks = function (t, runTask) {
var tasks = this.tasks;
var l = tasks.length;
var i = 0;
while (i < l && tasks[i].time <= t) {
++i;
}
this.tasks = tasks.slice(i);
// Run all ready tasks
for (var j = 0; j < i; ++j) {
this.tasks = runReadyTasks(runTask, tasks[j].events, this.tasks);
}
};
return TimelineImpl;
}());
function runReadyTasks(runTask, events, tasks) {
for (var i = 0; i < events.length; ++i) {
var task = events[i];
if (task.active) {
runTask(task);
// Reschedule periodic repeating tasks
// Check active again, since a task may have canceled itself
if (task.period >= 0 && task.active) {
task.time = task.time + task.period;
insertByTime(task, tasks);
}
}
}
return tasks;
}
function insertByTime(task, timeslots) {
var l = timeslots.length;
var time = getTime(task);
if (l === 0) {
timeslots.push(newTimeslot(time, [task]));
return;
}
var i = binarySearch(time, timeslots);
if (i >= l) {
timeslots.push(newTimeslot(time, [task]));
}
else {
insertAtTimeslot(task, timeslots, time, i);
}
}
function insertAtTimeslot(task, timeslots, time, i) {
var timeslot = timeslots[i];
if (time === timeslot.time) {
addEvent(task, timeslot.events);
}
else {
timeslots.splice(i, 0, newTimeslot(time, [task]));
}
}
function addEvent(task, events) {
if (events.length === 0 || task.time >= events[events.length - 1].time) {
events.push(task);
}
else {
spliceEvent(task, events);
}
}
function spliceEvent(task, events) {
for (var j = 0; j < events.length; j++) {
if (task.time < events[j].time) {
events.splice(j, 0, task);
break;
}
}
}
function getTime(scheduledTask) {
return Math.floor(scheduledTask.time);
}
/**
* @deprecated
*/
function removeAllFrom(f, timeslot) {
timeslot.events = prelude.removeAll(f, timeslot.events);
}
function binarySearch(t, sortedArray) {
var lo = 0;
var hi = sortedArray.length;
var mid, y;
while (lo < hi) {
mid = Math.floor((lo + hi) / 2);
y = sortedArray[mid];
if (t === y.time) {
return mid;
}
else if (t < y.time) {
hi = mid;
}
else {
lo = mid + 1;
}
}
return hi;
}
var newTimeslot = function (t, events) { return ({ time: t, events: events }); };
/** @license MIT License (c) copyright 2010-2017 original author or authors */
/* global setTimeout, clearTimeout */
var ClockTimer = /** @class */ (function () {
function ClockTimer(clock) {
this._clock = clock;
}
ClockTimer.prototype.now = function () {
return this._clock.now();
};
ClockTimer.prototype.setTimer = function (f, dt) {
return dt <= 0 ? runAsap(f) : setTimeout(f, dt);
};
ClockTimer.prototype.clearTimer = function (t) {
return t instanceof Asap ? t.cancel() : clearTimeout(t);
};
return ClockTimer;
}());
var Asap = /** @class */ (function () {
function Asap(f) {
this.f = f;
this.active = true;
}
Asap.prototype.run = function () {
if (this.active) {
return this.f();
}
};
Asap.prototype.error = function (e) {
throw e;
};
Asap.prototype.cancel = function () {
this.active = false;
};
return Asap;
}());
function runAsap(f) {
var task = new Asap(f);
defer(task);
return task;
}
/* global performance, process */
var RelativeClock = /** @class */ (function () {
function RelativeClock(clock, origin) {
this.origin = origin;
this.clock = clock;
}
RelativeClock.prototype.now = function () {
return this.clock.now() - this.origin;
};
return RelativeClock;
}());
var HRTimeClock = /** @class */ (function () {
function HRTimeClock(hrtime, origin) {
this.origin = origin;
this.hrtime = hrtime;
}
HRTimeClock.prototype.now = function () {
var hrt = this.hrtime(this.origin);
return (hrt[0] * 1e9 + hrt[1]) / 1e6;
};
return HRTimeClock;
}());
var clockRelativeTo = function (clock) {
return new RelativeClock(clock, clock.now());
};
var newPerformanceClock = function () {
return clockRelativeTo(performance);
};
/**
* @deprecated will be removed in 2.0.0
* Date.now is not monotonic, and performance.now is ubiquitous:
* @see https://caniuse.com/#search=performance.now
*/
var newDateClock = function () {
return clockRelativeTo(Date);
};
var newHRTimeClock = function () {
return new HRTimeClock(process.hrtime, process.hrtime());
};
var newPlatformClock = function () {
if (typeof performance !== 'undefined' && typeof performance.now === 'function') {
return newPerformanceClock();
}
else if (typeof process !== 'undefined' && typeof process.hrtime === 'function') {
return newHRTimeClock();
}
return newDateClock();
};
/**
* Read the current time from the provided Scheduler
*/
var currentTime = function (scheduler) {
return scheduler.currentTime();
};
/**
* Schedule a task to run as soon as possible, but
* not in the current call stack
*/
var asap = prelude.curry2(function (task, scheduler) {
return scheduler.scheduleTask(0, 0, -1, task);
});
/**
* Schedule a task to run after a millisecond delay
*/
var delay = prelude.curry3(function (delay, task, scheduler) {
return scheduler.scheduleTask(0, delay, -1, task);
});
/**
* Schedule a task to run periodically, with the
* first run starting asap
*/
var periodic = prelude.curry3(function (period, task, scheduler) {
return scheduler.scheduleTask(0, 0, period, task);
});
/**
* Cancel a scheduledTask
*/
var cancelTask = function (scheduledTask) {
return scheduledTask.dispose();
};
/**
* Cancel all ScheduledTasks for which a predicate is true
* @deprecated Will be removed in 2.0.0
*/
var cancelAllTasks = prelude.curry2(function (predicate, scheduler) {
console.warn("DEPRECATED cancelAllTasks to be removed in 2.0.0");
return scheduler.cancelAll(predicate);
});
var schedulerRelativeTo = prelude.curry2(function (offset, scheduler) {
return new RelativeScheduler(offset, scheduler);
});
/** @license MIT License (c) copyright 2010-2017 original author or authors */
var newScheduler = prelude.curry2(function (timer, timeline) { return new SchedulerImpl(timer, timeline); });
var newDefaultScheduler = function () { return new SchedulerImpl(newDefaultTimer(), new TimelineImpl()); };
var newDefaultTimer = function () { return new ClockTimer(newPlatformClock()); };
var newClockTimer = function (clock) { return new ClockTimer(clock); };
var newTimeline = function () { return new TimelineImpl(); };
exports.HRTimeClock = HRTimeClock;
exports.RelativeClock = RelativeClock;
exports.asap = asap;
exports.cancelAllTasks = cancelAllTasks;
exports.cancelTask = cancelTask;
exports.clockRelativeTo = clockRelativeTo;
exports.currentTime = currentTime;
exports.delay = delay;
exports.newClockTimer = newClockTimer;
exports.newDateClock = newDateClock;
exports.newDefaultScheduler = newDefaultScheduler;
exports.newDefaultTimer = newDefaultTimer;
exports.newHRTimeClock = newHRTimeClock;
exports.newPerformanceClock = newPerformanceClock;
exports.newPlatformClock = newPlatformClock;
exports.newScheduler = newScheduler;
exports.newTimeline = newTimeline;
exports.periodic = periodic;
exports.schedulerRelativeTo = schedulerRelativeTo;
Object.defineProperty(exports, '__esModule', { value: true });
})));
//# sourceMappingURL=index.js.map