wed
Version:
Wed is a schema-aware editor for XML documents.
157 lines • 5.7 kB
JavaScript
/**
* Task abstraction for wed.
* @author Louis-Dominique Dubeau
* @license MPL 2.0
* @copyright Mangalam Research Center for Buddhist Languages
*/
define(["require", "exports", "rxjs", "rxjs/operators"], function (require, exports, rxjs_1, operators_1) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
/**
* A task is a computation that should produce a definite goal after a finite
* time. This class is used to allow the task to happen in a way that does not
* completely block the JavaScript virtual machine. The task will happen in
* cycles that run for a maximum amount of time before relinquishing control.
*/
class TaskRunner {
/**
* @param task The computation controlled by this runner.
*
* @param options The options governing this runner.
*/
constructor(task, options = {}) {
this.task = task;
this._timeout = 0;
this._maxTimespan = 100;
this._boundWrapper = this._workWrapper.bind(this);
const keys = ["timeout", "maxTimespan"];
for (const key of keys) {
const value = options[key];
if (value === undefined) {
continue;
}
if (value < 0) {
throw new Error(`the value for ${key} cannot be negative`);
}
// tslint:disable-next-line:no-any
this[`_${key}`] = options[key];
}
this._state = new rxjs_1.BehaviorSubject({
running: false,
completed: false,
terminated: false,
});
this.state = this._state.asObservable();
}
get running() {
return this._state.value.running;
}
get completed() {
return this._state.value.completed;
}
get terminated() {
return this._state.value.terminated;
}
onCompleted() {
return this.state.pipe(operators_1.first((state) => state.completed)).toPromise();
}
_stateFieldChange(field, value) {
const latest = this._state.value;
const newState = Object.assign({}, this._state.value);
newState[field] = value;
if (newState[field] !== latest[field]) {
this._state.next(newState);
}
}
_setTimeoutId(value) {
this._timeoutId = value;
this._stateFieldChange("running", this._timeoutId !== undefined);
}
/**
* Marks the task as incomplete and starts processing.
*/
start() {
this.reset();
this.resume();
}
/**
* Resets the task to its initial state. The task will be deemed incomplete.
*/
reset() {
this._stateFieldChange("completed", false);
this.task.reset(this);
}
/**
* Resumes the task. This method does not change the completion status of the
* task. So it is possible to stop a task temporarily and resume it later from
* where it stopped.
*/
resume() {
if (this.completed) {
return;
}
if (this._timeoutId !== undefined) {
this.stop();
}
// When we call ``this.resume``, we want the task to resume ASAP. So we do
// not use ``this._timeout`` here. However, we do not call
// ``this._workWrapper`` directly because we want to be able to call
// ``this.resume`` from event handlers. If we did call ``this._workWrapper``
// directly, we'd be calling this._cycle from inside this._cycle
this._setTimeoutId(setTimeout(this._boundWrapper, 0));
}
/**
* Convenience method. The bound version of this method
* (``this._boundWrapper``) is what is called by the timeouts.
*/
_workWrapper() {
if (this._work()) {
this._setTimeoutId(setTimeout(this._boundWrapper, this._timeout));
}
else {
this._stateFieldChange("completed", true);
}
}
/**
* Keeps the task running by launching cycles only until done or until the
* maximum time span for one run is reached.
*
* @returns False if there is no more work to do. True otherwise.
*/
_work() {
const startDate = Date.now();
// tslint:disable-next-line:strict-boolean-expressions no-constant-condition
while (true) {
// Give a chance to other operations to work.
if ((this._maxTimespan > 0) &&
(Date.now() - startDate) >= this._maxTimespan) {
return true;
}
const ret = this.task.cycle(this);
if (!ret) {
return false;
}
}
}
/**
* Stops the task.
*/
stop() {
if (this._timeoutId !== undefined) {
clearTimeout(this._timeoutId);
}
this._setTimeoutId(undefined);
}
/**
* Terminate the task.
*/
terminate() {
this.stop();
this._stateFieldChange("terminated", true);
this._state.complete();
}
}
exports.TaskRunner = TaskRunner;
});
// LocalWords: MPL maxTimespan workWrapper
//# sourceMappingURL=task-runner.js.map