molstar
Version:
A comprehensive macromolecular library.
250 lines • 10.4 kB
JavaScript
/**
* Copyright (c) 2017-2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.ExecuteObservableChild = exports.ExecuteInContext = exports.CreateObservableCtx = exports.ExecuteObservable = void 0;
var tslib_1 = require("tslib");
var task_1 = require("../task");
var now_1 = require("../../mol-util/now");
var scheduler_1 = require("../util/scheduler");
var user_timing_1 = require("../util/user-timing");
var debug_1 = require("../../mol-util/debug");
function ExecuteObservable(task, observer, updateRateMs) {
if (updateRateMs === void 0) { updateRateMs = 250; }
var info = ProgressInfo(task, observer, updateRateMs);
var ctx = new ObservableRuntimeContext(info, info.root);
return execute(task, ctx);
}
exports.ExecuteObservable = ExecuteObservable;
function CreateObservableCtx(task, observer, updateRateMs) {
if (updateRateMs === void 0) { updateRateMs = 250; }
var info = ProgressInfo(task, observer, updateRateMs);
return new ObservableRuntimeContext(info, info.root);
}
exports.CreateObservableCtx = CreateObservableCtx;
function ExecuteInContext(ctx, task) {
return execute(task, ctx);
}
exports.ExecuteInContext = ExecuteInContext;
function ExecuteObservableChild(ctx, task, progress) {
return ctx.runChild(task, progress);
}
exports.ExecuteObservableChild = ExecuteObservableChild;
function defaultProgress(task) {
return {
taskId: task.id,
taskName: task.name,
message: '',
startedTime: 0,
canAbort: true,
isIndeterminate: true,
current: 0,
max: 0
};
}
function ProgressInfo(task, observer, updateRateMs) {
var abortToken = { abortRequested: false, treeAborted: false, reason: '' };
return {
updateRateMs: updateRateMs,
lastNotified: (0, now_1.now)(),
observer: observer,
abortToken: abortToken,
taskId: task.id,
root: { progress: defaultProgress(task), children: [] },
tryAbort: createAbortFunction(abortToken)
};
}
function createAbortFunction(token) {
return function (reason) {
token.abortRequested = true;
token.reason = reason || token.reason;
};
}
function cloneTree(root) {
return { progress: (0, tslib_1.__assign)({}, root.progress), children: root.children.map(cloneTree) };
}
function canAbort(root) {
return root.progress.canAbort && root.children.every(canAbort);
}
function snapshotProgress(info) {
return { root: cloneTree(info.root), canAbort: canAbort(info.root), requestAbort: info.tryAbort };
}
function execute(task, ctx) {
return (0, tslib_1.__awaiter)(this, void 0, void 0, function () {
var ret, e_1;
return (0, tslib_1.__generator)(this, function (_a) {
switch (_a.label) {
case 0:
user_timing_1.UserTiming.markStart(task);
ctx.node.progress.startedTime = (0, now_1.now)();
_a.label = 1;
case 1:
_a.trys.push([1, 3, , 7]);
return [4 /*yield*/, task.f(ctx)];
case 2:
ret = _a.sent();
user_timing_1.UserTiming.markEnd(task);
user_timing_1.UserTiming.measure(task);
if (ctx.info.abortToken.abortRequested) {
abort(ctx.info);
}
return [2 /*return*/, ret];
case 3:
e_1 = _a.sent();
if (!task_1.Task.isAbort(e_1)) return [3 /*break*/, 6];
ctx.isAborted = true;
if (!(ctx.node.children.length > 0)) return [3 /*break*/, 5];
return [4 /*yield*/, new Promise(function (res) { ctx.onChildrenFinished = res; })];
case 4:
_a.sent();
_a.label = 5;
case 5:
if (task.onAbort) {
task.onAbort();
}
_a.label = 6;
case 6:
if (debug_1.isDebugMode)
console.error(e_1);
throw e_1;
case 7: return [2 /*return*/];
}
});
});
}
function abort(info) {
if (!info.abortToken.treeAborted) {
info.abortToken.treeAborted = true;
abortTree(info.root);
notifyObserver(info, (0, now_1.now)());
}
throw task_1.Task.Aborted(info.abortToken.reason);
}
function abortTree(root) {
var progress = root.progress;
progress.isIndeterminate = true;
progress.canAbort = false;
progress.message = 'Aborting...';
for (var _a = 0, _b = root.children; _a < _b.length; _a++) {
var c = _b[_a];
abortTree(c);
}
}
// function shouldNotify(info: ProgressInfo, time: number) {
// return time - info.lastNotified > info.updateRateMs;
// }
function notifyObserver(info, time) {
info.lastNotified = time;
var snapshot = snapshotProgress(info);
info.observer(snapshot);
}
var ObservableRuntimeContext = /** @class */ (function () {
function ObservableRuntimeContext(info, node) {
this.isSynchronous = false;
this.isExecuting = true;
this.lastUpdatedTime = 0;
// used for waiting for cancelled computation trees
this.onChildrenFinished = void 0;
this.node = node;
this.info = info;
}
ObservableRuntimeContext.prototype.checkAborted = function () {
if (this.info.abortToken.abortRequested) {
this.isAborted = true;
abort(this.info);
}
};
Object.defineProperty(ObservableRuntimeContext.prototype, "shouldUpdate", {
get: function () {
this.checkAborted();
return (0, now_1.now)() - this.lastUpdatedTime > this.info.updateRateMs;
},
enumerable: false,
configurable: true
});
ObservableRuntimeContext.prototype.updateProgress = function (update) {
this.checkAborted();
if (!update)
return;
var progress = this.node.progress;
if (typeof update === 'string') {
progress.message = update;
progress.isIndeterminate = true;
}
else {
if (typeof update.canAbort !== 'undefined')
progress.canAbort = update.canAbort;
if (typeof update.message !== 'undefined')
progress.message = update.message;
if (typeof update.current !== 'undefined')
progress.current = update.current;
if (typeof update.max !== 'undefined')
progress.max = update.max;
progress.isIndeterminate = typeof progress.current === 'undefined' || typeof progress.max === 'undefined';
if (typeof update.isIndeterminate !== 'undefined')
progress.isIndeterminate = update.isIndeterminate;
}
};
ObservableRuntimeContext.prototype.update = function (progress, dontNotify) {
// The progress tracking and observer notification are separated
// because the computation can have a tree structure.
// All nodes of the tree should be regualarly updated at the specified frequency,
// however, the notification should only be invoked once per the whole tree.
this.lastUpdatedTime = (0, now_1.now)();
this.updateProgress(progress);
// TODO: do the shouldNotify check here?
if (!!dontNotify /* || !shouldNotify(this.info, this.lastUpdatedTime)*/)
return;
notifyObserver(this.info, this.lastUpdatedTime);
// The computation could have been aborted during the notifycation phase.
this.checkAborted();
return scheduler_1.Scheduler.immediatePromise();
};
ObservableRuntimeContext.prototype.runChild = function (task, progress) {
return (0, tslib_1.__awaiter)(this, void 0, void 0, function () {
var node, children, ctx, e_2, idx, i, _i;
return (0, tslib_1.__generator)(this, function (_a) {
switch (_a.label) {
case 0:
this.updateProgress(progress);
node = { progress: defaultProgress(task), children: [] };
children = this.node.children;
children.push(node);
ctx = new ObservableRuntimeContext(this.info, node);
_a.label = 1;
case 1:
_a.trys.push([1, 3, 4, 5]);
return [4 /*yield*/, execute(task, ctx)];
case 2: return [2 /*return*/, _a.sent()];
case 3:
e_2 = _a.sent();
if (task_1.Task.isAbort(e_2)) {
// need to catch the error here because otherwise
// promises for running child tasks in a tree-like computation
// will get orphaned and cause "uncaught error in Promise".
if (this.isAborted)
return [2 /*return*/, void 0];
}
throw e_2;
case 4:
idx = children.indexOf(node);
if (idx >= 0) {
for (i = idx, _i = children.length - 1; i < _i; i++) {
children[i] = children[i + 1];
}
children.pop();
}
if (children.length === 0 && this.onChildrenFinished)
this.onChildrenFinished();
return [7 /*endfinally*/];
case 5: return [2 /*return*/];
}
});
});
};
return ObservableRuntimeContext;
}());
//# sourceMappingURL=observable.js.map
;