monkeydo/monkey/Monkey.js
2021-11-10 12:31:04 +01:00

85 lines
No EOL
2 KiB
JavaScript

// Dedicated worker (monkey) that executes tasks from a Monkeydo manifest
importScripts("https://unpkg.com/comlink/dist/umd/comlink.js");
class Monkey {
constructor() {
this.flags = new Uint8ClampedArray(3);
this.tasks = [];
this.tasksLength = 0;
this.i = 0;
// Runtime task queue
this.queue = {
thisTask: null,
nextTask: null
}
}
// Task scheduler
next() {
if(this.flags[0] === 0 || this.flags[2] === 0) return this.abort();
const task = this.tasks[this.i];
// Run task after delay
this.queue.thisTask = setTimeout(() => {
// Dispatch task to main thread
postMessage(["TASK",task]);
this.i++;
},task[0]);
// Loop until flag is 0 or infinite if 255
if(this.i === this.tasksLength) {
this.i = -1;
if(this.flags[1] < 255) this.flags[2]--;
}
// Queue the next task
this.queue.nextTask = setTimeout(() => this.next(),task[0]);
}
abort() {
this.flags[2] = 0; // Playing: false
clearTimeout(this.queue.thisTask);
clearTimeout(this.queue.nextTask);
this.queue.thisTask = null;
this.queue.nextTask = null;
}
// Set or get a runtime flag
flag(index,value = null) {
return value ? this.flags[index] = value : this.flags[index];
}
// Fetch and install manifest from URL
async fetchManifest(url) {
const manifest = await fetch(url);
if(!manifest.ok) {
console.error("Monkeydo fetch error:",manifest);
throw new Error("Server responded with an error");
};
const json = await manifest.json();
return await this.loadManifest(json);
}
// Install a Monkeydo manifest
async loadManifest(manifest) {
return await new Promise((resolve,reject) => {
if(typeof manifest !== "object") {
try {
manifest = JSON.parse(manifest);
}
catch {
reject("Failed to load manifest");
}
}
this.tasks = manifest.tasks;
// Store length as property so we don't have to calculate the offset each iteration of next()
this.tasksLength = manifest.tasks.length - 1;
this.flags[0] = 1; // Manifest loaded: true
resolve();
});
}
}
Comlink.expose(Monkey);