Skip to content

Instantly share code, notes, and snippets.

@michaelhelvey
Created April 21, 2024 13:45
Show Gist options
  • Save michaelhelvey/68a8de59c2fa5e702d36d0759b620419 to your computer and use it in GitHub Desktop.
Save michaelhelvey/68a8de59c2fa5e702d36d0759b620419 to your computer and use it in GitHub Desktop.
Simple Event Loop
const events = [];
const tasks = [];
function registerInterest(event, task) {
events.push({ event, task });
}
function waitForEvents() {
if (events.length === 0) {
return;
}
console.log("waiting for events for %s seconds", 1);
blockFor(1);
let entry = events.pop();
while (entry) {
tasks.push(entry.task);
entry = events.pop();
}
}
function blockFor(seconds) {
const now = new Date().getTime();
const goal = seconds * 1000 + now;
while (new Date().getTime() < goal) {}
}
const files = {
1: { fd: 1, contents: "contents of file 1" },
2: { fd: 2, contents: "contents of file 2" },
};
function readFile(fd) {
const isReady = Math.random() >= 0.5;
if (isReady) {
return files[fd].contents;
}
return "would_block";
}
class Task {
constructor(fd) {
this.fileDescriptor = fd;
this.state = { type: "init" };
}
poll() {
// Loop until we return either ready or pending:
for (;;) {
switch (this.state.type) {
case "init": {
console.log({ task: this.fileDescriptor, state: "init" });
// Ask to be notified when something has happened to our file:
registerInterest({ type: "ReadFile", fileDescriptor: this.fileDescriptor }, this);
this.state = { type: "waiting_for_file", fileDescriptor: this.fileDescriptor };
return "pending";
}
case "waiting_for_file": {
console.log({ task: this.fileDescriptor, state: "waiting_for_file" });
// Check in on the actual state of our file:
const fileContents = readFile(this.state.fileDescriptor);
// If it would still block, we just say "too bad, wake me up next time", and go back to
// sleep by retunring "pending"
if (fileContents === "would_block") {
registerInterest({ type: "ReadFile", fileDescriptor: this.fileDescriptor }, this);
return "pending";
}
// Otherwise, we successfully got some bytes out of our file, so we're done
this.state = { type: "done", fileContents };
break;
}
case "done": {
// Here, we don't register interest in anything, so the event system will never schedule
// us again after this.
console.log({ task: this.fileDescriptor, state: "done" });
console.log(
"Task %d read file contents: %s",
this.fileDescriptor,
this.state.fileContents,
);
return "ready";
}
}
}
}
}
tasks.push(new Task(1));
tasks.push(new Task(2));
for (;;) {
console.log("event loop tick");
let currentTask = tasks.shift();
while (currentTask) {
currentTask.poll();
currentTask = tasks.shift();
}
waitForEvents();
if (tasks.length === 0) {
break;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment