Streaming HTML Data with Node and the Browser: A Beginner's Guide

Streaming HTML Data with Node and the Browser: A Beginner's Guide


JavaScript

Once I questioned how streaming HTML data works with a web browser. So in this blog, I am going to write about how it works.

So what does streaming HTML data (in my mind) look like?

Technically, what I want to do is, send a set of small chunks of HTML document (line by line) slowly so that I can see what’s happening in my web browser as it receives the data.

It looks like as follows:

`<html><head></head>`

// 1s later...

`<h1>Hello world</h1>`

// 1s later...

</html>

// done!

It turns out, in Express.js, res (response object) is an instance of Node.js’s http.serverResponse class. So res.write() method writes some arbitrary data to a stream - great!

Also, it’s interesting to add some JavaScript code to see how it works when we put either async or defer attribute to the <script> tag.

Let’s do it!

Here is my code.

const express = require("express");
const app = express();
const port = 3000;

async function waitFor(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

app.use(express.static("public")); // contains a js file

app.get("/", async (req, res) => {
  res.write(`<html>`);
  res.write(`<head>
  <script src="script.js" async></script>
  </head><body>`);

  for (let i = 0; i < 5; i++) {
    await waitFor(500);
    res.write(`<p>${i}</p>`);
  }
  res.write(`</body></html>`);
  res.end();
});

app.listen(port, () => {
  console.log(`Example app listening at http://localhost:${port}`);
});
  • 1. As soon as my server receives an HTTP request it only sends html-head tag back to the client.
  • 2. Then in every 0.5 secs, it sends a p tag with an arbitrary index number.
  • 3. After sending 5 of them, closing html tag will be sent to the client.

And public folder just contains a script.js file with a single line statement as follows:

console.log("invoked");

OK, so I’m ready to kick it off.

Open a web browser and run devtool.

As you can see, although my server doesn’t send a closing html tag, the browser automatically append the tag (so that we can see the HTML document in the browser even though it’s incomplete). This does make sense as (long time ago) it was recommended every <script> tag should be placed at the end of <body> tag.

On the other hand, the <script> tag with defer attribute doesn’t get invoked until the server finishes sending streaming data.

This means the browser emits onload event after receiving the whole data from the server.

And as you may expect, <script> tag with async attribute runs itself right after the download.

This experiment doesn’t deal with the latest web technologies at all - but should be the first step to understand the current frontend frameworks (namely Next.js, Remix, etc.). I’ll keep digging into it.

Thanks for reading ✌️

© 2024 Hiro