Created
April 20, 2024 20:41
-
-
Save EmNudge/2a5353488a41b8ea543cf5b7e0f1d31b to your computer and use it in GitHub Desktop.
Out Of Order Streaming in Node.js
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
import { createServer } from "node:http"; | |
const sleep = (ms) => new Promise((res) => setTimeout(res, ms)); | |
const suspend = (placeholder, promise) => { | |
const id = "stream-" + Math.random().toString(32).slice(2); | |
const inserterTag = `<script | |
src="data:text/javascript," | |
onload="window["${id}"].replaceWith(this.previousElementSibling), this.remove()" | |
></script>`; | |
const p = promise.then((content) => `${content}${inserterTag}`); | |
p[Symbol.toPrimitive] = () => | |
`<content-placeholder id="${id}">${placeholder}</content-placeholder>`; | |
return p; | |
}; | |
async function* getStreamingChunks(strings, ...values) { | |
yield String.raw(strings, ...values); | |
const chunkPromises = values.filter((value) => value instanceof Promise); | |
const resolvers = []; | |
const promiseQueue = Array(chunkPromises.length) | |
.fill() | |
.map(() => new Promise((res) => resolvers.push(res))); | |
for (const chunkPromise of chunkPromises) { | |
chunkPromise.then((content) => resolvers.shift()(content)); | |
} | |
for await (const resolvedChunk of promiseQueue) { | |
yield resolvedChunk; | |
} | |
} | |
createServer(async (_req, res) => { | |
res.writeHead(200, { | |
"Content-Type": "text/html", | |
"Transfer-Encoding": "chunked", | |
}); | |
const chunks = getStreamingChunks` | |
<html> | |
<head> | |
<title>Hello Page!</title> | |
</head> | |
<body> | |
<header>This is the Header</header> | |
<main> | |
${suspend( | |
"<h1>This is going to change</h1>", | |
sleep(1000).then(() => "<h1>Hello World!</h1>") | |
)} | |
${suspend( | |
"<p>This is going to change</p>", | |
sleep(500).then(() => "<p>Hello this is a paragraph</p>") | |
)} | |
</main> | |
<footer>This is the footer</footer> | |
</body> | |
</html>`; | |
for await (const chunk of chunks) { | |
res.write(chunk); | |
} | |
res.end(); | |
}).listen(8080); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Credit to LankyMoose and Kai for help with construction.