diff --git a/src/examples/computation.ts b/src/examples/computation.ts index 6e4d33d9d4590e968b3f3a822128eda980782803..00d2dd2f82c60a801e0baaee7bf00ac10b43caca 100644 --- a/src/examples/computation.ts +++ b/src/examples/computation.ts @@ -12,6 +12,10 @@ async function test() { console.log(r); } +function delay(ms: number) { + return new Promise(r => setTimeout(r, ms)); +} + function messageTree(root: Progress.Node, prefix = ''): string { if (!root.children.length) return `${prefix}${root.progress.message}`; @@ -20,25 +24,30 @@ function messageTree(root: Progress.Node, prefix = ''): string { return `${prefix}${root.progress.message}\n${subTree.join('\n')}`; } -function createTask<T>(delay: number, r: T): Task<T> { +function createTask<T>(delayMs: number, r: T): Task<T> { return Task.create('delayed', async ctx => { - await new Promise(r => setTimeout(r, delay)); - if (ctx.requiresUpdate) await ctx.update({ message: 'hello from delayed...' }); + ctx.updateProgress('Processing delayed... ' + r); + await delay(delayMs); + if (ctx.needsYield) await ctx.yield({ message: 'hello from delayed... ' + r }); return r; }); } async function testObs() { const t = Task.create('test o', async ctx => { - await new Promise(r => setTimeout(r, 250)); - if (ctx.requiresUpdate) await ctx.update({ message: 'hi! 1' }); - await new Promise(r => setTimeout(r, 125)); - if (ctx.requiresUpdate) await ctx.update({ message: 'hi! 2' }); - await new Promise(r => setTimeout(r, 250)); - if (ctx.requiresUpdate) await ctx.update({ message: 'hi! 3' }); - - const r = await ctx.runChild({ message: 'Running child!' }, createTask(250, 100)); - if (ctx.requiresUpdate) await ctx.update({ message: 'Almost done...' }); + await delay(250); + if (ctx.needsYield) await ctx.yield({ message: 'hi! 1' }); + await delay(125); + if (ctx.needsYield) await ctx.yield({ message: 'hi! 2' }); + await delay(250); + if (ctx.needsYield) await ctx.yield('hi! 3'); + + ctx.updateProgress('Running children...'); + const c1 = ctx.runChild(createTask(250, 1)); + const c2 = ctx.runChild(createTask(500, 2)); + const c3 = ctx.runChild(createTask(750, 3)); + const r = await c1 + await c2 + await c3; + if (ctx.needsYield) await ctx.yield({ message: 'Almost done...' }); return r + 1; }); const r = await Run(t, p => console.log(messageTree(p.root)), 250); diff --git a/src/mol-task/execution/observable.ts b/src/mol-task/execution/observable.ts index ef594fd840ec0e30124b42b7c23bc4dda21beb56..d2139c207669f1c22c986b0fca36640d6122a089 100644 --- a/src/mol-task/execution/observable.ts +++ b/src/mol-task/execution/observable.ts @@ -116,20 +116,26 @@ class ObservableRuntimeContext implements RuntimeContext { } } - get requiresUpdate(): boolean { + get needsYield(): boolean { this.checkAborted(); return now() - this.info.lastUpdated > this.info.updateRateMs; } - private setProgress(update: Partial<RuntimeContext.ProgressUpdate>) { + private setProgress(update?: string | Partial<RuntimeContext.ProgressUpdate>) { this.checkAborted(); + if (!update) return; + const progress = this.node.progress; - 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; + 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; + } } private resume = () => { @@ -137,7 +143,11 @@ class ObservableRuntimeContext implements RuntimeContext { this.lastScheduledTime = now(); } - update(progress: Partial<RuntimeContext.ProgressUpdate>): Promise<void> { + updateProgress(progress?: string | Partial<RuntimeContext.ProgressUpdate>) { + this.setProgress(progress); + } + + yield(progress?: string | Partial<RuntimeContext.ProgressUpdate>): Promise<void> { this.isExecuting = false; this.setProgress(progress); this.info.lastUpdated = now(); @@ -146,7 +156,7 @@ class ObservableRuntimeContext implements RuntimeContext { return ImmediateScheduler.last(this.resume); } - async runChild<T>(progress: Partial<RuntimeContext.ProgressUpdate>, task: Task<T>): Promise<T> { + async runChild<T>(task: Task<T>, progress?: string | Partial<RuntimeContext.ProgressUpdate>): Promise<T> { this.setProgress(progress); const node: Progress.Node = { progress: defaultProgress(this.info.taskId, task), children: [] }; const children = this.node.children as Progress.Node[]; diff --git a/src/mol-task/execution/runtime-context.ts b/src/mol-task/execution/runtime-context.ts index 7613790d3b3d5b9ef5581f5a3dbe082fc9fe9f1c..754f18a0dcb4b13f8b82425612afea3e287c878e 100644 --- a/src/mol-task/execution/runtime-context.ts +++ b/src/mol-task/execution/runtime-context.ts @@ -7,12 +7,13 @@ import Task from '../task' interface RuntimeContext { - readonly requiresUpdate: boolean, + readonly needsYield: boolean, + updateProgress(progress: string | Partial<RuntimeContext.ProgressUpdate>): void, // Idiomatic usage: - // if (ctx.requiresUpdate) await ctx.update({ ... }); - update(progress: Partial<RuntimeContext.ProgressUpdate>): Promise<void>, + // if (ctx.needsYield) await ctx.yield({ ... }); + yield(progress?: string | Partial<RuntimeContext.ProgressUpdate>): Promise<void>, // Force the user to pass the progress so that the progress tree can be kept in a "good state". - runChild<T>(progress: Partial<RuntimeContext.ProgressUpdate>, task: Task<T>): Promise<T> + runChild<T>(task: Task<T>, progress?: string | Partial<RuntimeContext.ProgressUpdate>): Promise<T> } namespace RuntimeContext { diff --git a/src/mol-task/execution/synchronous.ts b/src/mol-task/execution/synchronous.ts index 3d0f1f2c0de45c7dfd49caab26e87251a548588c..0267a36eab364fc89170167263a51e0a62a03e3b 100644 --- a/src/mol-task/execution/synchronous.ts +++ b/src/mol-task/execution/synchronous.ts @@ -10,9 +10,10 @@ import RuntimeContext from './runtime-context' const voidPromise = Promise.resolve(void 0); class SynchronousRuntimeContext implements RuntimeContext { - requiresUpdate: boolean = false; - update(progress: Partial<RuntimeContext.ProgressUpdate>): Promise<void> { return voidPromise; } - runChild<T>(progress: Partial<RuntimeContext.ProgressUpdate>, task: Task<T>): Promise<T> { return ExecuteSynchronous(task); } + needsYield: boolean = false; + updateProgress(progress: string | Partial<RuntimeContext.ProgressUpdate>): void { } + yield(progress?: string | Partial<RuntimeContext.ProgressUpdate>): Promise<void> { return voidPromise; } + runChild<T>(task: Task<T>, progress?: string | Partial<RuntimeContext.ProgressUpdate>): Promise<T> { return ExecuteSynchronous(task); } } const SyncRuntimeInstance = new SynchronousRuntimeContext();