Skip to content
Snippets Groups Projects
Commit f0b7aa07 authored by Zepei Xu's avatar Zepei Xu
Browse files

Merge branch 'master' into mol2-2

parents 45a947d8 5a0ef763
No related branches found
No related tags found
No related merge requests found
Showing
with 666 additions and 297 deletions
File suppressed by a .gitattributes entry, the file's encoding is unsupported, or the file size exceeds the limit.
...@@ -33,24 +33,24 @@ ...@@ -33,24 +33,24 @@
"author": "", "author": "",
"license": "MIT", "license": "MIT",
"devDependencies": { "devDependencies": {
"@types/benchmark": "^1.0.30", "@types/benchmark": "^1.0.31",
"@types/express": "^4.0.39", "@types/express": "^4.0.39",
"@types/jest": "^21.1.5", "@types/jest": "^21.1.8",
"@types/node": "^8.0.47", "@types/node": "^8.0.56",
"@types/node-fetch": "^1.6.7", "@types/node-fetch": "^1.6.7",
"benchmark": "^2.1.4", "benchmark": "^2.1.4",
"download-cli": "^1.0.5", "download-cli": "^1.0.5",
"jest": "^21.2.1", "jest": "^21.2.1",
"rollup": "^0.50.0", "rollup": "^0.50.1",
"rollup-plugin-buble": "^0.16.0", "rollup-plugin-buble": "^0.16.0",
"rollup-plugin-commonjs": "^8.2.6", "rollup-plugin-commonjs": "^8.2.6",
"rollup-plugin-json": "^2.3.0", "rollup-plugin-json": "^2.3.0",
"rollup-plugin-node-resolve": "^3.0.0", "rollup-plugin-node-resolve": "^3.0.0",
"rollup-watch": "^4.3.1", "rollup-watch": "^4.3.1",
"ts-jest": "^21.1.4", "ts-jest": "^21.2.4",
"tslint": "^5.8.0", "tslint": "^5.8.0",
"typescript": "^2.6.1", "typescript": "^2.6.2",
"uglify-js": "^3.1.7", "uglify-js": "^3.2.1",
"util.promisify": "^1.0.0" "util.promisify": "^1.0.0"
}, },
"dependencies": { "dependencies": {
......
// TODO /**
\ No newline at end of file * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { Task, Run, Progress, Scheduler, now } from 'mol-task'
export async function test1() {
const t = Task.create('test', async () => 1);
const r = await Run(t);
console.log(r);
}
function messageTree(root: Progress.Node, prefix = ''): string {
if (!root.children.length) return `${prefix}${root.progress.taskName}: ${root.progress.message}`;
const newPrefix = prefix + ' |_ ';
const subTree = root.children.map(c => messageTree(c, newPrefix));
return `${prefix}${root.progress.taskName}: ${root.progress.message}\n${subTree.join('\n')}`;
}
function createTask<T>(delayMs: number, r: T): Task<T> {
return Task.create('delayed value ' + r, async ctx => {
ctx.update('Processing delayed... ' + r, true);
await Scheduler.delay(delayMs);
if (ctx.shouldUpdate) await ctx.update({ message: 'hello from delayed... ' });
return r;
}, () => console.log('On abort called ' + r));
}
export function abortAfter(delay: number) {
return Task.create('abort after ' + delay, async ctx => {
await Scheduler.delay(delay);
throw Task.Aborted('test');
//if (ctx.shouldUpdate) await ctx.update({ message: 'hello from delayed... ' });
//return r;
});
}
function testTree() {
return Task.create('test o', async ctx => {
await Scheduler.delay(250);
if (ctx.shouldUpdate) await ctx.update({ message: 'hi! 1' });
await Scheduler.delay(125);
if (ctx.shouldUpdate) await ctx.update({ message: 'hi! 2' });
await Scheduler.delay(250);
if (ctx.shouldUpdate) await ctx.update('hi! 3');
// ctx.update('Running children...', true);
const c1 = ctx.runChild(createTask(250, 1));
const c2 = ctx.runChild(createTask(500, 2));
const c3 = ctx.runChild(createTask(750, 3));
//await ctx.runChild(abortAfter(350));
const r = await c1 + await c2 + await c3;
if (ctx.shouldUpdate) await ctx.update({ message: 'Almost done...' });
return r + 1;
});
}
export function abortingObserver(p: Progress) {
console.log(messageTree(p.root));
if (now() - p.root.progress.startedTime > 1000) {
p.requestAbort('test');
}
}
async function test() {
try {
//const r = await Run(testTree(), p => console.log(messageTree(p.root)), 250);
const r = await Run(testTree(), abortingObserver, 250);
console.log(r);
} catch (e) {
console.error(e);
}
}
test();
//testObs();
\ No newline at end of file
/**
* Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
export * from './generic/hash-set'
export * from './generic/linked-list'
export * from './generic/unique-array'
\ No newline at end of file
/**
* Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { LinkedList } from '../linked-list'
describe('linked list', () => {
function toArray<T>(list: LinkedList<T>) {
const ret: T[] = [];
for (let t = list.first; !!t; t = t.next) {
ret[ret.length] = t.value;
}
return ret;
}
function create<T>(xs: T[]) {
const list = LinkedList<T>();
for (const x of xs) list.addLast(x);
return list;
}
it('add', () => {
const list = LinkedList<number>();
list.addFirst(1);
list.addLast(2);
list.addFirst(3);
list.addFirst(4);
list.addLast(5);
expect(toArray(list)).toEqual([4, 3, 1, 2, 5]);
expect(list.count).toBe(5);
});
it ('remove', () => {
const list = create([1, 2, 3, 4]);
let fst = list.removeFirst();
expect(fst).toBe(1);
expect(list.last!.value).toBe(4);
expect(list.count).toBe(3);
expect(toArray(list)).toEqual([2, 3, 4]);
let last = list.removeLast();
expect(last).toBe(4);
expect(list.last!.value).toBe(3);
expect(list.count).toBe(2);
expect(toArray(list)).toEqual([2, 3]);
let n3 = list.find(3)!;
list.remove(n3);
expect(list.first!.value).toBe(2);
expect(list.last!.value).toBe(2);
expect(list.count).toBe(1);
expect(toArray(list)).toEqual([2]);
list.removeFirst();
expect(list.first).toBe(null);
expect(list.last).toBe(null);
expect(list.count).toBe(0);
expect(toArray(list)).toEqual([]);
})
});
\ No newline at end of file
File moved
/**
* Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
interface LinkedList<T> {
readonly count: number,
readonly first: LinkedList.Node<T> | null,
readonly last: LinkedList.Node<T> | null,
addFirst(value: T): LinkedList.Node<T>,
addLast(value: T): LinkedList.Node<T>,
remove(node: LinkedList.Node<T>): void,
removeFirst(): T | undefined,
removeLast(): T | undefined,
find(value: T): LinkedList.Node<T> | undefined
}
function LinkedList<T>(): LinkedList<T> {
return new LinkedListImpl();
}
namespace LinkedList {
export interface Node<T> {
previous: Node<T> | null,
next: Node<T> | null,
inList: boolean,
value: T
}
}
function createListNode<T>(value: T): LinkedList.Node<T> {
return { previous: null, next: null, inList: true, value };
}
class LinkedListImpl<T> implements LinkedList<T> {
count: number = 0;
first: LinkedList.Node<T> | null = null;
last: LinkedList.Node<T> | null = null;
addFirst(value: T) {
const node = createListNode(value);
node.inList = true;
if (this.first) this.first.previous = node;
node.next = this.first;
this.first = node;
this.count++;
if (!this.last) this.last = node;
return node;
}
addLast(value: T) {
const node = createListNode(value);
if (this.last !== null) {
this.last.next = node;
}
node.previous = this.last;
this.last = node;
if (this.first === null) {
this.first = node;
}
node.inList = true;
this.count++;
return node;
}
removeFirst(): T | undefined {
const fst = this.first;
if (fst) {
this.remove(fst);
return fst.value;
}
return void 0;
}
removeLast(): T | undefined {
const last = this.last;
if (last) {
this.remove(last);
return last.value;
}
return void 0;
}
remove(node: LinkedList.Node<T>) {
if (!node.inList) return;
node.inList = false;
if (node.previous !== null) {
node.previous.next = node.next;
}
else if (/*first == item*/ node.previous === null) {
this.first = node.next;
}
if (node.next !== null) {
node.next.previous = node.previous;
}
else if (/*last == item*/ node.next === null) {
this.last = node.previous;
}
node.next = null;
node.previous = null;
this.count--;
}
find(value: T): LinkedList.Node<T> | undefined {
let current = this.first;
while (current !== null) {
if (current.value === value) return current;
current = current.next;
}
return void 0;
}
}
export { LinkedList }
\ No newline at end of file
...@@ -8,5 +8,6 @@ import * as DB from './db' ...@@ -8,5 +8,6 @@ import * as DB from './db'
import * as Int from './int' import * as Int from './int'
import Iterator from './iterator' import Iterator from './iterator'
import * as Util from './util' import * as Util from './util'
import * as Generic from './generic'
export { DB, Int, Iterator, Util } export { DB, Int, Iterator, Util, Generic }
\ No newline at end of file \ No newline at end of file
...@@ -5,8 +5,6 @@ ...@@ -5,8 +5,6 @@
*/ */
export * from './util/chunked-array' export * from './util/chunked-array'
export * from './util/unique-array'
export * from './util/hash-set'
export * from './util/equivalence-classes' export * from './util/equivalence-classes'
export * from './util/hash-functions' export * from './util/hash-functions'
export * from './util/sort' export * from './util/sort'
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
* @author David Sehnal <david.sehnal@gmail.com> * @author David Sehnal <david.sehnal@gmail.com>
*/ */
import { HashSet } from 'mol-data/util' import { HashSet } from 'mol-data/generic'
import { Structure, AtomSet } from '../structure' import { Structure, AtomSet } from '../structure'
// A selection is a pair of a Structure and a sequence of unique AtomSets // A selection is a pair of a Structure and a sequence of unique AtomSets
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
*/ */
import { OrderedSet, Iterator } from 'mol-data/int' import { OrderedSet, Iterator } from 'mol-data/int'
import { UniqueArray } from 'mol-data/util' import { UniqueArray } from 'mol-data/generic'
import SymmetryOperator from 'mol-math/geometry/symmetry-operator' import SymmetryOperator from 'mol-math/geometry/symmetry-operator'
import { Model, Format } from '../model' import { Model, Format } from '../model'
import Unit from './unit' import Unit from './unit'
......
/**
* Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* Adapted from https://github.com/dsehnal/LiteMol
* @author David Sehnal <david.sehnal@gmail.com>
*/
import Scheduler from './scheduler'
import timeNow from './time'
interface Computation<A> {
(ctx?: Computation.Context): Promise<A>
}
namespace Computation {
export let PRINT_ERRORS_TO_CONSOLE = false;
export function create<A>(computation: (ctx: Context) => Promise<A>) {
return ComputationImpl(computation);
}
export function resolve<A>(a: A) {
return create<A>(_ => Promise.resolve(a));
}
export function reject<A>(reason: any) {
return create<A>(_ => Promise.reject(reason));
}
export interface Params {
updateRateMs?: number,
observer?: ProgressObserver
}
export const Aborted = 'Aborted';
export interface Progress {
message: string,
isIndeterminate: boolean,
current: number,
max: number,
elapsedMs: number,
requestAbort?: () => void
}
export interface ProgressUpdate {
message?: string,
abort?: boolean | (() => void),
current?: number,
max?: number
}
export interface Context {
readonly isSynchronous: boolean,
/** Also checks if the computation was aborted. If so, throws. */
readonly requiresUpdate: boolean,
requestAbort(): void,
subscribe(onProgress: ProgressObserver): { dispose: () => void },
/** Also checks if the computation was aborted. If so, throws. */
update(info: ProgressUpdate): Promise<void> | void
}
export type ProgressObserver = (progress: Readonly<Progress>) => void;
const emptyDisposer = { dispose: () => { } }
/** A context without updates. */
export const synchronous: Context = {
isSynchronous: true,
requiresUpdate: false,
requestAbort() { },
subscribe(onProgress) { return emptyDisposer; },
update(info) { }
}
export function observable(params?: Partial<Params>) {
const ret = new ObservableContext(params && params.updateRateMs);
if (params && params.observer) ret.subscribe(params.observer);
return ret;
}
export const now = timeNow;
/** A utility for splitting large computations into smaller parts. */
export interface Chunker {
setNextChunkSize(size: number): void,
/** nextChunk must return the number of actually processed chunks. */
process(nextChunk: (chunkSize: number) => number, update: (updater: Context['update']) => void, nextChunkSize?: number): Promise<void>
}
export function chunker(ctx: Context, nextChunkSize: number): Chunker {
return new ChunkerImpl(ctx, nextChunkSize);
}
}
const DefaulUpdateRateMs = 150;
function ComputationImpl<A>(computation: (ctx: Computation.Context) => Promise<A>): Computation<A> {
return (ctx?: Computation.Context) => {
const context: ObservableContext = ctx ? ctx : Computation.synchronous as any;
return new Promise<A>(async (resolve, reject) => {
try {
if (context.started) context.started();
const result = await computation(context);
resolve(result);
} catch (e) {
if (Computation.PRINT_ERRORS_TO_CONSOLE) console.error(e);
reject(e);
} finally {
if (context.finished) context.finished();
}
});
}
}
class ObservableContext implements Computation.Context {
readonly updateRate: number;
readonly isSynchronous: boolean = false;
private level = 0;
private startedTime = 0;
private abortRequested = false;
private lastUpdated = 0;
private observers: Computation.ProgressObserver[] | undefined = void 0;
private progress: Computation.Progress = { message: 'Working...', current: 0, max: 0, elapsedMs: 0, isIndeterminate: true, requestAbort: void 0 };
private checkAborted() {
if (this.abortRequested) throw Computation.Aborted;
}
private abortRequester = () => { this.abortRequested = true };
subscribe = (obs: Computation.ProgressObserver) => {
if (!this.observers) this.observers = [];
this.observers.push(obs);
return {
dispose: () => {
if (!this.observers) return;
for (let i = 0; i < this.observers.length; i++) {
if (this.observers[i] === obs) {
this.observers[i] = this.observers[this.observers.length - 1];
this.observers.pop();
return;
}
}
}
};
}
requestAbort() {
try {
if (this.abortRequester) {
this.abortRequester.call(null);
}
} catch (e) { }
}
update({ message, abort, current, max }: Computation.ProgressUpdate): Promise<void> | void {
this.checkAborted();
const time = Computation.now();
if (typeof abort === 'boolean') {
this.progress.requestAbort = abort ? this.abortRequester : void 0;
} else {
if (abort) this.abortRequester = abort;
this.progress.requestAbort = abort ? this.abortRequester : void 0;
}
if (typeof message !== 'undefined') this.progress.message = message;
this.progress.elapsedMs = time - this.startedTime;
if (isNaN(current!)) {
this.progress.isIndeterminate = true;
} else {
this.progress.isIndeterminate = false;
this.progress.current = current!;
if (!isNaN(max!)) this.progress.max = max!;
}
if (this.observers) {
const p = { ...this.progress };
for (let i = 0, _i = this.observers.length; i < _i; i++) {
Scheduler.immediate(this.observers[i], p);
}
}
this.lastUpdated = time;
return Scheduler.immediatePromise();
}
get requiresUpdate() {
this.checkAborted();
if (this.isSynchronous) return false;
return Computation.now() - this.lastUpdated > this.updateRate;
}
started() {
if (!this.level) {
this.startedTime = Computation.now();
this.lastUpdated = this.startedTime;
}
this.level++;
}
finished() {
this.level--;
if (this.level < 0) {
throw new Error('Bug in code somewhere, Computation.resolve/reject called too many times.');
}
if (!this.level) this.observers = void 0;
}
constructor(updateRate?: number) {
this.updateRate = updateRate || DefaulUpdateRateMs;
}
}
class ChunkerImpl implements Computation.Chunker {
private processedSinceUpdate = 0;
private updater: Computation.Context['update'];
private computeChunkSize(delta: number) {
if (!delta) {
this.processedSinceUpdate = 0;
return this.nextChunkSize;
}
const rate = (this.context as ObservableContext).updateRate || DefaulUpdateRateMs;
const ret = Math.round(this.processedSinceUpdate * rate / delta + 1);
this.processedSinceUpdate = 0;
return ret;
}
private getNextChunkSize() {
const ctx = this.context as ObservableContext;
// be smart if the computation is synchronous and process the whole chunk at once.
if (ctx.isSynchronous) return Number.MAX_SAFE_INTEGER;
return this.nextChunkSize;
}
setNextChunkSize(size: number) {
this.nextChunkSize = size;
}
async process(nextChunk: (size: number) => number, update: (updater: Computation.Context['update']) => Promise<void> | void, nextChunkSize?: number) {
if (typeof nextChunkSize !== 'undefined') this.setNextChunkSize(nextChunkSize);
this.processedSinceUpdate = 0;
// track time for the actual computation and exclude the "update time"
let chunkStart = Computation.now();
let lastChunkSize: number;
let chunkCount = 0;
let totalSize = 0;
let updateCount = 0;
while ((lastChunkSize = nextChunk(this.getNextChunkSize())) > 0) {
chunkCount++;
this.processedSinceUpdate += lastChunkSize;
totalSize += lastChunkSize;
if (this.context.requiresUpdate) {
let time = Computation.now();
await update(this.updater);
this.nextChunkSize = updateCount > 0
? Math.round((totalSize + this.computeChunkSize(time - chunkStart)) / (chunkCount + 1))
: this.computeChunkSize(time - chunkStart)
updateCount++;
chunkStart = Computation.now();
}
}
if (this.context.requiresUpdate) {
let time = Computation.now();
await update(this.updater);
this.nextChunkSize = updateCount > 0
? Math.round((totalSize + this.computeChunkSize(time - chunkStart)) / (chunkCount + 1))
: this.computeChunkSize(time - chunkStart)
}
}
constructor(public context: Computation.Context, private nextChunkSize: number) {
this.updater = this.context.update.bind(this.context);
}
}
export default Computation;
\ No newline at end of file
/**
* Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { Task } from '../task'
import { RuntimeContext } from './runtime-context'
import { Progress } from './progress'
import { now } from '../util/now'
import { Scheduler } from '../util/scheduler'
function ExecuteObservable<T>(task: Task<T>, observer: Progress.Observer, updateRateMs = 250) {
const info = ProgressInfo(task, observer, updateRateMs);
const ctx = new ObservableRuntimeContext(info, info.root);
return execute(task, ctx);
}
namespace ExecuteObservable {
export let PRINT_ERRORS_TO_STD_ERR = false;
}
function defaultProgress(task: Task<any>): Task.Progress {
return {
taskId: task.id,
taskName: task.name,
message: '',
startedTime: 0,
canAbort: true,
isIndeterminate: true,
current: 0,
max: 0
};
}
interface ProgressInfo {
updateRateMs: number,
lastNotified: number,
observer: Progress.Observer,
abortToken: { abortRequested: boolean, treeAborted: boolean, reason: string },
taskId: number;
root: Progress.Node;
tryAbort: (reason?: string) => void;
}
function ProgressInfo(task: Task<any>, observer: Progress.Observer, updateRateMs: number): ProgressInfo {
const abortToken: ProgressInfo['abortToken'] = { abortRequested: false, treeAborted: false, reason: '' };
return {
updateRateMs,
lastNotified: now(),
observer,
abortToken,
taskId: task.id,
root: { progress: defaultProgress(task), children: [] },
tryAbort: createAbortFunction(abortToken)
};
}
function createAbortFunction(token: ProgressInfo['abortToken']) {
return (reason?: string) => {
token.abortRequested = true;
token.reason = reason || token.reason;
};
}
function cloneTree(root: Progress.Node): Progress.Node {
return { progress: { ...root.progress }, children: root.children.map(cloneTree) };
}
function canAbort(root: Progress.Node): boolean {
return root.progress.canAbort && root.children.every(canAbort);
}
function snapshotProgress(info: ProgressInfo): Progress {
return { root: cloneTree(info.root), canAbort: canAbort(info.root), requestAbort: info.tryAbort };
}
async function execute<T>(task: Task<T>, ctx: ObservableRuntimeContext) {
ctx.node.progress.startedTime = now();
try {
const ret = await task.__f(ctx);
if (ctx.info.abortToken.abortRequested) {
abort(ctx.info, ctx.node);
}
return ret;
} catch (e) {
if (Task.isAbort(e)) {
// wait for all child computations to go thru the abort phase.
if (ctx.node.children.length > 0) {
await new Promise(res => { ctx.onChildrenFinished = res; });
}
if (task.__onAbort) task.__onAbort();
}
if (ExecuteObservable.PRINT_ERRORS_TO_STD_ERR) console.error(e);
throw e;
}
}
function abort(info: ProgressInfo, node: Progress.Node) {
if (!info.abortToken.treeAborted) {
info.abortToken.treeAborted = true;
abortTree(info.root);
notifyObserver(info, now());
}
throw Task.Aborted(info.abortToken.reason);
}
function abortTree(root: Progress.Node) {
const progress = root.progress;
progress.isIndeterminate = true;
progress.canAbort = false;
progress.message = 'Aborting...';
for (const c of root.children) abortTree(c);
}
function shouldNotify(info: ProgressInfo, time: number) {
return time - info.lastNotified > info.updateRateMs;
}
function notifyObserver(info: ProgressInfo, time: number) {
info.lastNotified = time;
const snapshot = snapshotProgress(info);
info.observer(snapshot);
}
class ObservableRuntimeContext implements RuntimeContext {
isExecuting = true;
lastUpdatedTime = 0;
node: Progress.Node;
info: ProgressInfo;
// used for waiting for cancelled computation trees
onChildrenFinished?: () => void = void 0;
private checkAborted() {
if (this.info.abortToken.abortRequested) {
abort(this.info, this.node);
}
}
get shouldUpdate(): boolean {
this.checkAborted();
return now() - this.lastUpdatedTime > this.info.updateRateMs;
}
private updateProgress(update?: string | Partial<RuntimeContext.ProgressUpdate>) {
this.checkAborted();
if (!update) return;
const progress = this.node.progress;
if (typeof update === 'string') {
progress.message = update;
} else {
if (typeof update.canAbort !== 'undefined') progress.canAbort = update.canAbort;
if (typeof update.current !== 'undefined') progress.current = update.current;
if (typeof update.isIndeterminate !== 'undefined') progress.isIndeterminate = update.isIndeterminate;
if (typeof update.max !== 'undefined') progress.max = update.max;
if (typeof update.message !== 'undefined') progress.message = update.message;
}
}
update(progress?: string | Partial<RuntimeContext.ProgressUpdate>, dontNotify?: boolean): Promise<void> | void {
// 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 = now();
this.updateProgress(progress);
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.immediatePromise();
}
async runChild<T>(task: Task<T>, progress?: string | Partial<RuntimeContext.ProgressUpdate>): Promise<T> {
this.updateProgress(progress);
// Create a new child context and add it to the progress tree.
// When the child task finishes, remove the tree node.
const node: Progress.Node = { progress: defaultProgress(task), children: [] };
const children = this.node.children as Progress.Node[];
children.push(node);
const ctx = new ObservableRuntimeContext(this.info, node);
try {
return await execute(task, ctx);
} catch (e) {
if (Task.isAbort(e)) {
// 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".
return void 0 as any;
}
throw e;
} finally {
// remove the progress node after the computation has finished.
const idx = children.indexOf(node);
if (idx >= 0) {
for (let i = idx, _i = children.length - 1; i < _i; i++) {
children[i] = children[i + 1];
}
children.pop();
}
if (children.length === 0 && this.onChildrenFinished) this.onChildrenFinished();
}
}
constructor(info: ProgressInfo, node: Progress.Node) {
this.node = node;
this.info = info;
}
}
export { ExecuteObservable }
\ No newline at end of file
/**
* Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { Task } from '../task'
interface Progress {
root: Progress.Node,
canAbort: boolean,
requestAbort: (reason?: string) => void
}
namespace Progress {
export interface Node {
readonly progress: Task.Progress,
readonly children: ReadonlyArray<Node>
}
export interface Observer { (progress: Progress): void }
}
export { Progress }
\ No newline at end of file
/**
* Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { Task } from '../task'
interface RuntimeContext {
readonly shouldUpdate: boolean,
// Idiomatic usage:
// if (ctx.needsYield) await ctx.yield({ ... });
//
// Alternatively, progress can be updated without notifying (and yielding) using update(progress, true).
// This is useful for nested tasks.
update(progress?: string | Partial<RuntimeContext.ProgressUpdate>, dontNotify?: boolean): Promise<void> | void,
// Run a child task that adds a new node to the progress tree.
// Allow to pass the progress so that the progress tree can be kept in a "good state" without having to separately call update.
runChild<T>(task: Task<T>, progress?: string | Partial<RuntimeContext.ProgressUpdate>): Promise<T>
}
namespace RuntimeContext {
export interface AbortToken { isAborted: boolean }
export interface ProgressUpdate {
message: string,
isIndeterminate: boolean,
current: number,
max: number,
canAbort: boolean
}
}
export { RuntimeContext }
\ No newline at end of file
/**
* Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { Task } from '../task'
import { RuntimeContext } from './runtime-context'
class SynchronousRuntimeContext implements RuntimeContext {
shouldUpdate: boolean = false;
update(progress: string | Partial<RuntimeContext.ProgressUpdate>, dontNotify?: boolean): Promise<void> | void { }
runChild<T>(task: Task<T>, progress?: string | Partial<RuntimeContext.ProgressUpdate>): Promise<T> { return ExecuteSynchronous(task); }
}
const SyncRuntimeInstance = new SynchronousRuntimeContext();
function ExecuteSynchronous<T>(task: Task<T>) {
return task.__f(SyncRuntimeInstance);
}
export { ExecuteSynchronous }
\ No newline at end of file
/**
* Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { Task } from './task'
import { RuntimeContext } from './execution/runtime-context'
import { ExecuteSynchronous } from './execution/synchronous'
import { ExecuteObservable } from './execution/observable'
import { Progress } from './execution/progress'
import { now } from './util/now'
import { Scheduler } from './util/scheduler'
// Run the task without the ability to observe its progress.
function Run<T>(task: Task<T>): Promise<T>;
// Run the task with the ability to observe its progress and request cancellation.
function Run<T>(task: Task<T>, observer: Progress.Observer, updateRateMs?: number): Promise<T>;
function Run<T>(task: Task<T>, observer?: Progress.Observer, updateRateMs?: number): Promise<T> {
if (observer) return ExecuteObservable(task, observer, updateRateMs || 250);
return ExecuteSynchronous(task);
}
export { Task, RuntimeContext, Progress, Run, now, Scheduler }
\ No newline at end of file
/**
* Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { RuntimeContext } from './execution/runtime-context'
// A "named function wrapper" with built in "computation tree progress tracking".
// Use Run(t, ?observer, ?updateRate) to execute
interface Task<T> {
readonly id: number,
readonly name: string,
// Do not call this directly, use Run.
readonly __f: (ctx: RuntimeContext) => Promise<T>,
// Do not call this directly, use Run.
readonly __onAbort: (() => void) | undefined
}
namespace Task {
export interface Aborted { isAborted: true, reason: string }
export function isAbort(e: any): e is Aborted { return !!e && !!e.isAborted; }
export function Aborted(reason: string): Aborted { return { isAborted: true, reason }; }
export function create<T>(name: string, f: (ctx: RuntimeContext) => Promise<T>, onAbort?: () => void): Task<T> {
return { id: nextId(), name, __f: f, __onAbort: onAbort };
}
export function constant<T>(name: string, value: T): Task<T> { return create(name, async ctx => value); }
export function fail(name: string, reason: string): Task<any> { return create(name, async ctx => { throw new Error(reason); }); }
export interface Progress {
taskId: number,
taskName: string,
startedTime: number,
message: string,
canAbort: boolean,
isIndeterminate: boolean,
current: number,
max: number
}
let _id = 0;
function nextId() {
const ret = _id;
_id = (_id + 1) % 0x3fffffff;
return ret;
}
}
export { Task }
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment