monkeydo/do/Monkey.js
Victor Westerlund b2556f1ced Added flag queue
Calls sent to setFlag before a monkey has been spawned will be queued until the next GIVE_MANIFEST event is received.

- Replaced this.monkey with let monkey
2021-10-18 13:12:04 +02:00

134 lines
No EOL
2.8 KiB
JavaScript

// Dedicated worker which executes tasks from a Monkeydo manifest
class Monkey {
constructor(manifest) {
const self = this;
this.tasks = manifest.tasks;
this.tasksLength = this.tasks.length - 1;
this.flags = {
playing: 0,
stacking: 0, // Subsequent calls to play() will build a queue (jQuery-style)
loop: 0, // Loop n times; <0 = infinite
_forwards: 1, // Playback direction
set forwards(forwards = true) {
if(forwards == this._forwards) {
return false;
}
// Toggle playback direction
self.tasks = self.tasks.reverse();
this._forwards = 1 - this._forwards;
},
get forwards() {
return this._forwards;
}
}
this.i = 0; // Manifest iterator index
this.queue = {
task: null,
next: null
}
Object.seal(this.queue);
}
// Pass task to main thread for execution
run(task) {
postMessage(["TASK",task]);
this.i++;
}
// Interrupt timeout and put monkey to sleep
interrupt() {
clearTimeout(this.queue.task);
clearTimeout(this.queue.next);
this.queue.task = null;
this.queue.next = null;
this.flags.playing = 0;
}
play() {
// Stack playback as loops if flag is set
if(this.flags.playing) {
if(this.flags.stacking && this.flags.loop >= 0) {
this.flags.loop++;
}
return;
}
this.queueNext();
}
// Schedule task for execution by index
queueNext() {
this.flags.playing = 1;
const data = this.tasks[this.i];
const task = {
wait: data[0],
func: data[1],
args: data.slice(2)
};
// Schedule the current task to run after the specified wait time
this.queue.task = setTimeout(() => this.run(task),task.wait);
// We're out of tasks to schedule..
if(this.i >= this.tasksLength) {
this.i = -1;
// Exit if we're out of loops
if(this.flags.loop === 0) {
this.flags.playing = 0;
return false;
}
// Decrement loop iterations if not infinite (negative int)
if(this.flags.loop > 0) {
this.flags.loop--;
}
}
// Run this function again when the scheduled task will fire
this.queue.next = setTimeout(() => this.queueNext(),task.wait);
}
}
// Unspawned monkey target
let monkey = undefined;
// Event handler for messages received from initiator
onmessage = (message) => {
const type = message.data[0] ? message.data[0] : message.data;
const data = message.data[1];
switch(type) {
// Attempt to load manfiest provided by initiator thread
case "GIVE_MANIFEST":
try {
monkey = new Monkey(data);
postMessage(["RECEIVED_MANIFEST","OK"]);
}
catch(error) {
postMessage(["RECEIVED_MANIFEST",error]);
}
break;
case "SET_PLAYING":
if(data === true) {
monkey.play();
return;
}
monkey.interrupt();
break;
case "GET_FLAG":
const flag = monkey.flags[data];
postMessage(parseInt(flag));
break;
case "SET_FLAG":
monkey.flags[data[0]] = data[1];
break;
default: return; // No op
}
}