mirror of
https://codeberg.org/vlw/monkeydo.git
synced 2025-09-13 23:53:41 +02:00
Compare commits
23 commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
6b18c90228 | ||
![]() |
46f3a07c85 | ||
![]() |
a6e51c1e48 | ||
![]() |
22eda97800 | ||
![]() |
9e362617e9 | ||
539690cbdd | |||
675fe748e4 | |||
86ea8cd031 | |||
ff0ed25a3b | |||
dd071ea8bb | |||
4073785f93 | |||
b2556f1ced | |||
c713bb8755 | |||
f53bea079e | |||
32493065fc | |||
d1b04e406b | |||
3ab606641e | |||
f6e86a5892 | |||
17224b0a86 | |||
c275186126 | |||
ef09a568e0 | |||
4765f9dc17 | |||
6991138f85 |
6 changed files with 369 additions and 194 deletions
123
Monkeydo.mjs
123
Monkeydo.mjs
|
@ -1,104 +1,43 @@
|
||||||
import { default as MonkeyWorker } from "./worker/TaskManager.mjs";
|
import { default as MonkeyMaster } from "./monkey/MonkeyMaster.mjs";
|
||||||
|
|
||||||
export default class Monkeydo extends MonkeyWorker {
|
export default class Monkeydo extends MonkeyMaster {
|
||||||
constructor(manifest = false) {
|
constructor(methods) {
|
||||||
|
if(typeof methods !== "object") {
|
||||||
|
throw new TypeError(`Expected type 'object' but got '${typeof methods}' when initializing Monkeydo`);
|
||||||
|
}
|
||||||
super();
|
super();
|
||||||
this.monkeydo = {
|
this.methods = {};
|
||||||
version: "0.1",
|
Object.assign(this.methods,methods);
|
||||||
debugLevel: 0,
|
|
||||||
// Flag if debugging is enabled, regardless of level
|
|
||||||
get debug() {
|
|
||||||
return this.debugLevel > 0 ? true : false;
|
|
||||||
},
|
|
||||||
// Set debug level. Non-verbose debugging by default
|
|
||||||
set debug(level = 1) {
|
|
||||||
this.debugLevel = level;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Object.seal(this.monkeydo);
|
|
||||||
|
|
||||||
// Monkeydo manifest parsed with load()
|
|
||||||
this.manifest = {
|
|
||||||
header: null,
|
|
||||||
body: null
|
|
||||||
};
|
|
||||||
|
|
||||||
if(!window.Worker) {
|
|
||||||
throw new Error("JavaScript Workers aren't supported by your browser");
|
|
||||||
}
|
|
||||||
|
|
||||||
if(manifest) {
|
|
||||||
this.load(manifest);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
debug(attachment = "DEBUG_EMPTY") {
|
// Execute a task
|
||||||
if(this.monkeydo.debug) {
|
do(task) {
|
||||||
console.warn("-- Monkeydo debug -->",attachment);
|
if(!task[1] in this.methods) return;
|
||||||
return;
|
const args = task.splice(2);
|
||||||
}
|
this.methods[task[1]]?.(...args);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load a Monkeydo manifest from JSON via string or URL
|
// Loop playback X times or negative number for infinite
|
||||||
|
async loop(times = 255) {
|
||||||
|
if(typeof times !== "number") {
|
||||||
|
times = parseInt(times);
|
||||||
|
}
|
||||||
|
times = Math.floor(times);
|
||||||
|
times = Math.min(Math.max(times,0),255); // Clamp number to 8 bits
|
||||||
|
return await this.setFlag("loop",times);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load Monkeydo manifest
|
||||||
async load(manifest) {
|
async load(manifest) {
|
||||||
const errorPrefix = "MANIFEST_IMPORT_FAILED: ";
|
if(typeof manifest === "object") {
|
||||||
let data;
|
manifest = JSON.stringify(manifest);
|
||||||
if(typeof manifest !== "string") {
|
|
||||||
this.debug(manifest);
|
|
||||||
throw new TypeError(errorPrefix + "Expected JSON or URL");
|
|
||||||
}
|
}
|
||||||
|
return await this.loadManifest(manifest);
|
||||||
// Attempt to parse the argument as JSON
|
|
||||||
try {
|
|
||||||
data = JSON.parse(manifest);
|
|
||||||
}
|
|
||||||
catch {
|
|
||||||
// If that fails, attempt to parse it as a URL
|
|
||||||
try {
|
|
||||||
manifest = new URL(manifest);
|
|
||||||
const fetchManifest = await fetch(manifest);
|
|
||||||
|
|
||||||
// If the URL parsed but the fetch response is invalid, give up and throw an error
|
|
||||||
if(!fetchManifest.ok || fetchManifest.headers.get("Content-Type") !== "application/json") {
|
|
||||||
throw new TypeError(errorPrefix + "Invalid response Content-Type or HTTP status");
|
|
||||||
}
|
|
||||||
data = await fetchManifest.json();
|
|
||||||
}
|
|
||||||
catch(error) {
|
|
||||||
this.debug(manifest);
|
|
||||||
if(!error instanceof TypeError) {
|
|
||||||
throw new TypeError(errorPrefix + "Invalid JSON or URL");
|
|
||||||
}
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure the parsed JSON is a valid Monkeydo manifest
|
|
||||||
if(!data.hasOwnProperty("header") || !data.hasOwnProperty("body")) {
|
|
||||||
this.debug(data);
|
|
||||||
throw new Error(errorPrefix + "Expected 'header' and 'body' properties in object");
|
|
||||||
}
|
|
||||||
|
|
||||||
this.manifest.header = data.header;
|
|
||||||
this.manifest.body = data.body;
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Execute tasks from Monkeydo manifest
|
async play(manifest = null) {
|
||||||
async do() {
|
if(!this.ready && !manifest) throw new Error("Can not start playback without a manifest");
|
||||||
const errorPrefix = "DO_FAILED: ";
|
if(manifest) await this.load(manifest);
|
||||||
// Abort if the manifest object doesn't contain any header data
|
return await this.start();
|
||||||
if(!this.manifest.header) {
|
|
||||||
this.debug(this.manifest.header);
|
|
||||||
throw new Error(errorPrefix + `Expected header object from contructed property`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hand over the loaded manifest to the MonkeyWorker task manager
|
|
||||||
const monkey = this.giveManifest();
|
|
||||||
monkey.then(() => this.play())
|
|
||||||
.catch(error => {
|
|
||||||
this.debug(error);
|
|
||||||
throw new Error(errorPrefix + "Failed to post manifest to worker thread");
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
111
README.md
Normal file
111
README.md
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
<p align="center">
|
||||||
|
<img width="400" src="https://storage.googleapis.com/public.victorwesterlund.com/github/VictorWesterlund/monkeydo/monkeydo_.svg"/>
|
||||||
|
</p>
|
||||||
|
<h3 align="center">Multithreaded web animations and task chaining</h3>
|
||||||
|
<hr>
|
||||||
|
<p align="center">Execute general purpose JavaScript on cue with greater performance. Monkeydo is great, and designed for, complex DOM animations.</p>
|
||||||
|
<p align="center"><img src="https://storage.googleapis.com/public.victorwesterlund.com/github/VictorWesterlund/monkeydo/simple_demo.gif"/></a>
|
||||||
|
<table>
|
||||||
|
<td>
|
||||||
|
<p align="center">Monkeydo JSON manifest<br><a href="#manifest-semantics">View semantics</a></p>
|
||||||
|
<pre lang="json">
|
||||||
|
{
|
||||||
|
"tasks": [
|
||||||
|
[2000,"moveTo",100,0],
|
||||||
|
[1500,"setColor","red"],
|
||||||
|
[2650,"setColor","blue"],
|
||||||
|
[550,"moveTo",350,0]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
</pre>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<p align="center">Normal JavaScript</p>
|
||||||
|
<pre lang="js">
|
||||||
|
const methods = {
|
||||||
|
element: document.getElementById("element"),
|
||||||
|
moveTo: (x,y) => {
|
||||||
|
methods.element.style.setProperty("transform",`translate(${x}%,${y}%)`);
|
||||||
|
},
|
||||||
|
setColor: (color) => {
|
||||||
|
methods.element.style.setProperty("background-color",color);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</pre>
|
||||||
|
</td>
|
||||||
|
</table>
|
||||||
|
<a href="https://victorwesterlund.github.io/monkeydo-demo/demo/simple_shapes">Open live demo</a>
|
||||||
|
<h1 align="center">Use Monkeydo</h1>
|
||||||
|
<p>Monkeydo comes as an ES6 module. In this guide we'll import this directly from a <i>./modules/</i> folder, but any location accessible by the importing script will work.</p>
|
||||||
|
<ol>
|
||||||
|
<li><strong>Import <code>Monkeydo</code> as an ESM</strong>
|
||||||
|
<pre lang="js">
|
||||||
|
import { default as Monkeydo } from "./modules/Monkeydo/Monkeydo.mjs";
|
||||||
|
</pre>
|
||||||
|
</li>
|
||||||
|
<li><strong>Define your JS methods in an object</strong>
|
||||||
|
<pre lang="js">
|
||||||
|
const methods = {
|
||||||
|
singForMe: (foo,bar) => {
|
||||||
|
console.log(foo,bar);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</pre>
|
||||||
|
</li>
|
||||||
|
<li><strong>Define your tasks in a JSON manifest (file or JSON-compatible JavaScript)</strong>
|
||||||
|
<pre lang="json">
|
||||||
|
{
|
||||||
|
"tasks": [
|
||||||
|
[0,"singForMe","Just like a","monkey"],
|
||||||
|
[1200,"singForMe","I've been","dancing"],
|
||||||
|
[160,"singForMe","my whole","life"]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
</pre>
|
||||||
|
</li>
|
||||||
|
<li><strong>Initialize and run <code>Monkeydo</code> with your methods and manifest</strong>
|
||||||
|
<pre lang="js">
|
||||||
|
const monkey = new Monkeydo(methods);
|
||||||
|
monkey.play(manifest);
|
||||||
|
</pre>
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
<p>The example above would be the same as running:</p>
|
||||||
|
<pre lang="js">
|
||||||
|
console.log("Just like a","monkey"); // Right away
|
||||||
|
console.log("I've been","dancing"); // 1.2 seconds after the first
|
||||||
|
console.log("my whole","life"); // and then 160 milliseconds after the second
|
||||||
|
</pre>
|
||||||
|
<h1>Manifest Semantics</h1>
|
||||||
|
<p>The JS passed to the Monkeydo constructor is executed by the initiator thread (ususally the main thread) when time is up. Which method and when is defined in a JSON file or string with the following semantics:</p>
|
||||||
|
<table>
|
||||||
|
<td>
|
||||||
|
<pre lang="json">
|
||||||
|
{
|
||||||
|
"tasks": [
|
||||||
|
[0,"myJavaSriptMethod","someArgument","anotherArgument"]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
</pre>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<table align="center">
|
||||||
|
<tr>
|
||||||
|
<th>Array key</th>
|
||||||
|
<th>Description</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align="center">0</td>
|
||||||
|
<td><strong>Delay</strong><br>Wait this many milliseconds before running this method</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align="center">1</td>
|
||||||
|
<td><strong>Method</strong><br>Name of the JavaScript method to call</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align="center">2+n</td>
|
||||||
|
<td><strong>Arguments</strong><br>Some amount of arguments to pass to the method</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</table>
|
92
monkey/Monkey.js
Normal file
92
monkey/Monkey.js
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
// 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 = {
|
||||||
|
tasks: [],
|
||||||
|
length: 0,
|
||||||
|
_target: 0,
|
||||||
|
_i: 0,
|
||||||
|
set manifest(manifest) {
|
||||||
|
this.tasks = manifest;
|
||||||
|
this.length = this.tasks.length - 1;
|
||||||
|
},
|
||||||
|
get task() {
|
||||||
|
return this.tasks[this._i];
|
||||||
|
},
|
||||||
|
get target() {
|
||||||
|
return this._target;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Advance to the next task or loop
|
||||||
|
next() {
|
||||||
|
// Reset index and loop if out of tasks
|
||||||
|
if(this.tasks._i >= this.tasks.length) {
|
||||||
|
this.tasks._i = -1;
|
||||||
|
if(this.flags[1] === 255) return; // Loop forever
|
||||||
|
this.flags[2] -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.tasks._i++;
|
||||||
|
const nextTask = this.tasks.task;
|
||||||
|
this.tasks._target = performance.now() + nextTask[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Main event loop, runs on every frame
|
||||||
|
tick() {
|
||||||
|
if(this === undefined) return;
|
||||||
|
if(this.flags[0] === 0 || this.flags[2] === 0) return this.abort();
|
||||||
|
|
||||||
|
const frame = Math.min(performance.now(),this.tasks.target);
|
||||||
|
if(frame == this.tasks.target) {
|
||||||
|
postMessage(["TASK",this.tasks.task]);
|
||||||
|
this.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
requestAnimationFrame(this.tick.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
abort() {
|
||||||
|
this.flags[2] = 0; // Playing: false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
if(typeof manifest !== "object") {
|
||||||
|
try {
|
||||||
|
manifest = JSON.parse(manifest);
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Promise.reject("Failed to load manifest");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.tasks.manifest = manifest.tasks;
|
||||||
|
this.flags[0] = 1; // Manifest loaded: true
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Comlink.expose(Monkey);
|
135
monkey/MonkeyMaster.mjs
Normal file
135
monkey/MonkeyMaster.mjs
Normal file
|
@ -0,0 +1,135 @@
|
||||||
|
// Task manager for Monkeydo dedicated workers (monkeys)
|
||||||
|
|
||||||
|
import * as Comlink from "https://unpkg.com/comlink/dist/esm/comlink.mjs";
|
||||||
|
|
||||||
|
export default class MonkeyMaster {
|
||||||
|
constructor() {
|
||||||
|
this.comlink = null;
|
||||||
|
|
||||||
|
this.ready = false;
|
||||||
|
// Tasks will be queued here on runtime if the worker isn't ready
|
||||||
|
this.queue = {
|
||||||
|
_flags: [],
|
||||||
|
set flag(flag) {
|
||||||
|
this._flags.push(flag);
|
||||||
|
},
|
||||||
|
// Attempt to send all queued flags
|
||||||
|
sendAllFlags: () => {
|
||||||
|
// Copy flags and clear queue
|
||||||
|
const flags = [...this.queue._flags];
|
||||||
|
this.queue._flags = [];
|
||||||
|
flags.forEach(flag => this.setFlag(...flag));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Import worker relative to this module
|
||||||
|
getWorkerPath() {
|
||||||
|
const name = "Monkey.js";
|
||||||
|
const url = new URL(import.meta.url);
|
||||||
|
|
||||||
|
// Replace pathname of this file with worker
|
||||||
|
const path = url.pathname.split("/");
|
||||||
|
path[path.length - 1] = name;
|
||||||
|
|
||||||
|
url.pathname = path.join("/");
|
||||||
|
return url.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
async init() {
|
||||||
|
// Spawn and wrap dedicated worker with Comlink
|
||||||
|
const worker = new Worker(this.getWorkerPath());
|
||||||
|
worker.addEventListener("message",event => {
|
||||||
|
if(event.data[0] !== "TASK") return;
|
||||||
|
this.do(event.data[1]); // Send inner array (task)
|
||||||
|
});
|
||||||
|
|
||||||
|
const Monkey = Comlink.wrap(worker);
|
||||||
|
this.comlink = await new Monkey();
|
||||||
|
|
||||||
|
// Wait for comlink to spin up
|
||||||
|
if(!this.comlink) Promise.reject("Failed to establish Comlink with worker");
|
||||||
|
|
||||||
|
this.ready = true;
|
||||||
|
// Send queued flags when worker is ready
|
||||||
|
this.queue.sendAllFlags();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return a flag array index by name
|
||||||
|
flagStringToIndex(flag) {
|
||||||
|
const flags = [
|
||||||
|
"MANIFEST_LOADED",
|
||||||
|
"LOOP",
|
||||||
|
"PLAYING"
|
||||||
|
];
|
||||||
|
|
||||||
|
// Translate string to index
|
||||||
|
if(typeof flag === "string" || flag < 0) {
|
||||||
|
flag = flags.indexOf(flag.toUpperCase());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that key is in bounds
|
||||||
|
if(flag < 0 || flags > flags.length - 1) return false;
|
||||||
|
return flag;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getFlag(flag) {
|
||||||
|
const key = this.flagStringToIndex(flag);
|
||||||
|
if(!key) Promise.reject("Invalid flag");
|
||||||
|
return await this.comlink.flag(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set or queue worker runtime flag
|
||||||
|
async setFlag(flag,value) {
|
||||||
|
const key = this.flagStringToIndex(flag);
|
||||||
|
if(!key) Promise.reject("Invalid flag");
|
||||||
|
|
||||||
|
// Set the flag when the worker is ready
|
||||||
|
if(!this.ready) {
|
||||||
|
this.queue.flag = [key,value];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tell worker to update flag by key
|
||||||
|
const update = await this.comlink.flag(key,value);
|
||||||
|
if(!update) {
|
||||||
|
this.queue.flag = [key,value];
|
||||||
|
}
|
||||||
|
return update;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load a Monkeydo manifest by URL or JSON string
|
||||||
|
async loadManifest(manifest) {
|
||||||
|
if(!this.ready) await this.init();
|
||||||
|
let load = null;
|
||||||
|
// Attempt load string as URL and fetch manifest
|
||||||
|
try {
|
||||||
|
const url = new URL(manifest);
|
||||||
|
// If the URL parsed but fetch failed, this promise will reject
|
||||||
|
load = this.comlink.fetchManifest(url.toString());
|
||||||
|
}
|
||||||
|
// Or attempt to load string as JSON if it's not a URL
|
||||||
|
catch {
|
||||||
|
load = this.comlink.loadManifest(manifest);
|
||||||
|
}
|
||||||
|
|
||||||
|
load.then(() => Promise.resolve())
|
||||||
|
.catch(() => Promise.reject("Failed to load manifest"));
|
||||||
|
}
|
||||||
|
|
||||||
|
async stop() {
|
||||||
|
return await this.comlink.abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start playback of a loaded manifest
|
||||||
|
async start() {
|
||||||
|
const playing = await this.getFlag("playing");
|
||||||
|
let loop = await this.getFlag("loop");
|
||||||
|
loop = loop > 0 ? loop : 1; // Play once if loop has no value
|
||||||
|
|
||||||
|
if(playing > 0) return;
|
||||||
|
await this.setFlag("playing",loop);
|
||||||
|
return await this.comlink.tick();
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,64 +0,0 @@
|
||||||
// Task scheduler and iterator of Monkeydo manifests
|
|
||||||
|
|
||||||
class Monkey {
|
|
||||||
constructor(manifest) {
|
|
||||||
this.data = manifest.body;
|
|
||||||
this.dataLength = this.data.length - 1;
|
|
||||||
|
|
||||||
this.i = 0;
|
|
||||||
this.queue = {
|
|
||||||
task: null,
|
|
||||||
next: null
|
|
||||||
}
|
|
||||||
Object.seal(this.queue);
|
|
||||||
}
|
|
||||||
|
|
||||||
run(data) {
|
|
||||||
this.i++;
|
|
||||||
postMessage(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
queueNext() {
|
|
||||||
const data = this.data[this.i];
|
|
||||||
this.queue.task = setTimeout(() => this.run(data.do),data.wait);
|
|
||||||
|
|
||||||
// Schedule next task if it's not the last
|
|
||||||
if(this.i >= this.dataLength) {
|
|
||||||
this.i = 0;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.queue.next = setTimeout(() => this.queueNext(),data.wait);
|
|
||||||
}
|
|
||||||
|
|
||||||
interrupt() {
|
|
||||||
clearTimeout(this.queue.task);
|
|
||||||
clearTimeout(this.queue.next);
|
|
||||||
this.queue.task = null;
|
|
||||||
this.queue.next = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Global event handler for this worker
|
|
||||||
onmessage = (message) => {
|
|
||||||
const type = message.data[0] ? message.data[0] : null;
|
|
||||||
const data = message.data[1];
|
|
||||||
|
|
||||||
switch(type) {
|
|
||||||
case "GIVE_MANIFEST":
|
|
||||||
try {
|
|
||||||
this.monkey = new Monkey(data);
|
|
||||||
postMessage("OK");
|
|
||||||
}
|
|
||||||
catch(error) {
|
|
||||||
postMessage(["MANIFEST_ERROR",error]);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "PLAYING":
|
|
||||||
this.monkey.queueNext();
|
|
||||||
break;
|
|
||||||
|
|
||||||
default: return; // No op
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,38 +0,0 @@
|
||||||
// Task manager for Monkeydo dedicated workers
|
|
||||||
|
|
||||||
export default class TaskManager {
|
|
||||||
constructor() {
|
|
||||||
// Get path of this file
|
|
||||||
this.ready = false;
|
|
||||||
let location = new URL(import.meta.url);
|
|
||||||
location = location.pathname.replace("TaskManager.mjs",""); // Get parent directory
|
|
||||||
|
|
||||||
// Spawn a dedicated worker for scheduling events from manifest
|
|
||||||
this.worker = new Worker(location + "Monkey.js");
|
|
||||||
}
|
|
||||||
|
|
||||||
play() {
|
|
||||||
this.worker.postMessage(["PLAYING",true]);
|
|
||||||
this.worker.addEventListener("message",message => eval(message.data));
|
|
||||||
}
|
|
||||||
|
|
||||||
pause() {
|
|
||||||
this.worker.postMessage(["PLAYING",false]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pass manifest to worker and await response
|
|
||||||
async giveManifest() {
|
|
||||||
this.worker.postMessage(["GIVE_MANIFEST",this.manifest]);
|
|
||||||
|
|
||||||
// Wait for the worker to install the manifest
|
|
||||||
const ack = await new Promise((resolve,reject) => {
|
|
||||||
this.worker.addEventListener("message",message => {
|
|
||||||
if(message.data !== "OK") {
|
|
||||||
reject(message.data);
|
|
||||||
}
|
|
||||||
resolve();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
return ack;
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Add table
Reference in a new issue