Skip to content
Snippets Groups Projects
Commit d46610f4 authored by David Sehnal's avatar David Sehnal
Browse files

Immediate scheduler

parent 261e9606
No related branches found
No related tags found
No related merge requests found
...@@ -107,7 +107,7 @@ async function runCIF(input: string | Uint8Array) { ...@@ -107,7 +107,7 @@ async function runCIF(input: string | Uint8Array) {
export function _cif() { export function _cif() {
let path = `./examples/1cbs_updated.cif`; let path = `./examples/1cbs_updated.cif`;
path = '../test/3j3q.cif'; path = 'c:/test/quick/3j3q.cif';
fs.readFile(path, 'utf8', function (err, input) { fs.readFile(path, 'utf8', function (err, input) {
if (err) { if (err) {
return console.log(err); return console.log(err);
......
...@@ -5,6 +5,8 @@ ...@@ -5,6 +5,8 @@
* @author David Sehnal <david.sehnal@gmail.com> * @author David Sehnal <david.sehnal@gmail.com>
*/ */
import Scheduler from './scheduler'
class Computation<A> { class Computation<A> {
run(ctx?: Computation.Context) { run(ctx?: Computation.Context) {
return this.runWithContext(ctx).result; return this.runWithContext(ctx).result;
...@@ -14,7 +16,7 @@ class Computation<A> { ...@@ -14,7 +16,7 @@ class Computation<A> {
const context = ctx ? ctx as Computation.ObservableContext : new Computation.ObservableContext(); const context = ctx ? ctx as Computation.ObservableContext : new Computation.ObservableContext();
return { return {
subscribe: (context as Computation.ObservableContext).subscribe || Helpers.NoOpSubscribe, subscribe: (context as Computation.ObservableContext).subscribe || NoOpSubscribe,
result: new Promise<A>(async (resolve, reject) => { result: new Promise<A>(async (resolve, reject) => {
try { try {
if (context.started) context.started(); if (context.started) context.started();
...@@ -35,6 +37,8 @@ class Computation<A> { ...@@ -35,6 +37,8 @@ class Computation<A> {
} }
} }
const NoOpSubscribe = () => {}
namespace Computation { namespace Computation {
const DefaulUpdateRateMs = 100; const DefaulUpdateRateMs = 100;
...@@ -123,7 +127,7 @@ namespace Computation { ...@@ -123,7 +127,7 @@ namespace Computation {
updateProgress(msg: string, abort?: boolean | (() => void), current?: number, max?: number): Promise<void> | void { updateProgress(msg: string, abort?: boolean | (() => void), current?: number, max?: number): Promise<void> | void {
this.checkAborted(); this.checkAborted();
const time = Helpers.getTime(); const time = now();
if (typeof abort === 'boolean') { if (typeof abort === 'boolean') {
this.progress.requestAbort = abort ? this.abortRequester : void 0; this.progress.requestAbort = abort ? this.abortRequester : void 0;
...@@ -134,7 +138,7 @@ namespace Computation { ...@@ -134,7 +138,7 @@ namespace Computation {
this.progress.message = msg; this.progress.message = msg;
this.progress.elapsedMs = time - this.startedTime; this.progress.elapsedMs = time - this.startedTime;
if (isNaN(current!)) { if (isNaN(current!) || isNaN(max!)) {
this.progress.isIndeterminate = true; this.progress.isIndeterminate = true;
} else { } else {
this.progress.isIndeterminate = false; this.progress.isIndeterminate = false;
...@@ -144,23 +148,23 @@ namespace Computation { ...@@ -144,23 +148,23 @@ namespace Computation {
if (this.observers) { if (this.observers) {
const p = { ...this.progress }; const p = { ...this.progress };
for (const o of this.observers) setTimeout(o, 0, p); for (const o of this.observers) Scheduler.immediate(o, p);
} }
this.lastDelta = time - this.lastUpdated; this.lastDelta = time - this.lastUpdated;
this.lastUpdated = time; this.lastUpdated = time;
return new Promise<void>(res => setTimeout(res, 0)); return Scheduler.immediatePromise();
} }
get requiresUpdate() { get requiresUpdate() {
this.checkAborted(); this.checkAborted();
if (this.isSynchronous) return false; if (this.isSynchronous) return false;
return Helpers.getTime() - this.lastUpdated > this.updateRate / 2; return now() - this.lastUpdated > this.updateRate / 2;
} }
started() { started() {
if (!this.level) this.startedTime = Helpers.getTime(); if (!this.level) this.startedTime = now();
this.level++; this.level++;
} }
...@@ -212,13 +216,11 @@ namespace Computation { ...@@ -212,13 +216,11 @@ namespace Computation {
this.currentChunkSize = defaultChunkSize; this.currentChunkSize = defaultChunkSize;
} }
} }
}
namespace Helpers {
declare var process: any; declare var process: any;
declare var window: any; declare var window: any;
export const getTime: () => number = (function () { export const now: () => number = (function () {
if (typeof window !== 'undefined' && window.performance) { if (typeof window !== 'undefined' && window.performance) {
const perf = window.performance; const perf = window.performance;
return function () { return perf.now(); } return function () { return perf.now(); }
...@@ -231,8 +233,6 @@ namespace Helpers { ...@@ -231,8 +233,6 @@ namespace Helpers {
return function () { return +new Date(); } return function () { return +new Date(); }
} }
})(); })();
export const NoOpSubscribe = () => {};
} }
export default Computation; export default Computation;
\ No newline at end of file
/*
* Copyright (c) 2017 molio contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
/**
* setImmediate polyfill adapted from https://github.com/YuzuJS/setImmediate
* Copyright (c) 2012 Barnesandnoble.com, llc, Donavon West, and Domenic Denicola
* MIT license.
*/
function createActions() {
type Callback = (...args: any[]) => void;
type Task = { callback: Callback, args: any[] }
const tasksByHandle: { [handle: number]: Task } = { };
const doc = typeof document !== 'undefined' ? document : void 0;
let currentlyRunningATask = false;
let nextHandle = 1; // Spec says greater than zero
let registerImmediate: ((handle: number) => void);
function setImmediate(callback: Callback, ...args: any[]) {
// Callback can either be a function or a string
if (typeof callback !== 'function') {
callback = new Function('' + callback) as Callback;
}
// Store and register the task
const task = { callback: callback, args: args };
tasksByHandle[nextHandle] = task;
registerImmediate(nextHandle);
return nextHandle++;
}
function clearImmediate(handle: number) {
delete tasksByHandle[handle];
}
function run(task: Task) {
const callback = task.callback;
const args = task.args;
switch (args.length) {
case 0:
callback();
break;
case 1:
callback(args[0]);
break;
case 2:
callback(args[0], args[1]);
break;
case 3:
callback(args[0], args[1], args[2]);
break;
default:
callback.apply(undefined, args);
break;
}
}
function runIfPresent(handle: number) {
// From the spec: 'Wait until any invocations of this algorithm started before this one have completed.'
// So if we're currently running a task, we'll need to delay this invocation.
if (currentlyRunningATask) {
// Delay by doing a setTimeout. setImmediate was tried instead, but in Firefox 7 it generated a
// 'too much recursion' error.
setTimeout(runIfPresent, 0, handle);
} else {
const task = tasksByHandle[handle];
if (task) {
currentlyRunningATask = true;
try {
run(task);
} finally {
clearImmediate(handle);
currentlyRunningATask = false;
}
}
}
}
function installNextTickImplementation() {
registerImmediate = function(handle) {
process.nextTick(function () { runIfPresent(handle); });
};
}
function canUsePostMessage() {
// The test against `importScripts` prevents this implementation from being installed inside a web worker,
// where `global.postMessage` means something completely different and can't be used for this purpose.
const global = window as any;
if (global && global.postMessage && !global.importScripts) {
let postMessageIsAsynchronous = true;
const oldOnMessage = global.onmessage;
global.onmessage = function() {
postMessageIsAsynchronous = false;
};
global.postMessage('', '*');
global.onmessage = oldOnMessage;
return postMessageIsAsynchronous;
}
}
function installPostMessageImplementation() {
// Installs an event handler on `global` for the `message` event: see
// * https://developer.mozilla.org/en/DOM/window.postMessage
// * http://www.whatwg.org/specs/web-apps/current-work/multipage/comms.html#crossDocumentMessages
const messagePrefix = 'setImmediate$' + Math.random() + '$';
const onGlobalMessage = function(event: any) {
if (event.source === global &&
typeof event.data === 'string' &&
event.data.indexOf(messagePrefix) === 0) {
runIfPresent(+event.data.slice(messagePrefix.length));
}
};
if (window.addEventListener) {
window.addEventListener('message', onGlobalMessage, false);
} else {
(window as any).attachEvent('onmessage', onGlobalMessage);
}
registerImmediate = function(handle) {
window.postMessage(messagePrefix + handle, '*');
};
}
function installMessageChannelImplementation() {
const channel = new MessageChannel();
channel.port1.onmessage = function(event) {
const handle = event.data;
runIfPresent(handle);
};
registerImmediate = function(handle) {
channel.port2.postMessage(handle);
};
}
function installReadyStateChangeImplementation() {
const html = doc!.documentElement;
registerImmediate = function(handle) {
// Create a <script> element; its readystatechange event will be fired asynchronously once it is inserted
// into the document. Do so, thus queuing up the task. Remember to clean up once it's been called.
let script = doc!.createElement('script') as any;
script.onreadystatechange = function () {
runIfPresent(handle);
script.onreadystatechange = null;
html.removeChild(script);
script = null;
};
html.appendChild(script);
};
}
function installSetTimeoutImplementation() {
registerImmediate = function(handle) {
setTimeout(runIfPresent, 0, handle);
};
}
// If supported, we should attach to the prototype of global, since that is where setTimeout et al. live.
//const attachTo = Object.getPrototypeOf && Object.getPrototypeOf(global);
//attachTo = attachTo && attachTo.setTimeout ? attachTo : global;
// Don't get fooled by e.g. browserify environments.
if (typeof process !== 'undefined' && {}.toString.call(process) === '[object process]') {
// For Node.js before 0.9
installNextTickImplementation();
} else if (canUsePostMessage()) {
// For non-IE10 modern browsers
installPostMessageImplementation();
} else if (typeof MessageChannel !== 'undefined') {
// For web workers, where supported
installMessageChannelImplementation();
} else if (doc && 'onreadystatechange' in doc.createElement('script')) {
// For IE 6–8
installReadyStateChangeImplementation();
} else {
// For older browsers
installSetTimeoutImplementation();
}
return {
setImmediate,
clearImmediate
};
}
const actions = (function () {
if (typeof setImmediate !== 'undefined') {
return { setImmediate, clearImmediate };
}
return createActions();
}());
function resolveImmediate(res: () => void) {
actions.setImmediate(res);
}
export default {
immediate: actions.setImmediate,
clearImmediate: actions.clearImmediate,
immediatePromise() { return new Promise<void>(resolveImmediate); }
};
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
"sourceMap": false, "sourceMap": false,
"noUnusedLocals": true, "noUnusedLocals": true,
"strictNullChecks": true, "strictNullChecks": true,
"lib": [ "es6" ], "lib": [ "es6", "dom" ],
"outDir": "build/js/src" "outDir": "build/js/src"
}, },
"include": [ "src/**/*" ] "include": [ "src/**/*" ]
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment