Created
April 21, 2024 13:45
-
-
Save michaelhelvey/68a8de59c2fa5e702d36d0759b620419 to your computer and use it in GitHub Desktop.
Simple Event Loop
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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