Complete Intro to Real-Time

Coding a HTTP/2 Push Frontend

Brian Holt

Brian Holt

SQLite Cloud
Complete Intro to Real-Time

Check out a free preview of the full Complete Intro to Real-Time course

The "Coding a HTTP/2 Push Frontend" Lesson is part of the full, Complete Intro to Real-Time course featured in this preview video. Here's what you'd learn in this lesson:

Brian codes the front-end logic to read and decode the data stream coming from the server. Each chunk of data is parsed and rendered on the screen. This logic will run continuously in a do/while loop until the connection with the server is closed.


Transcript from the "Coding a HTTP/2 Push Frontend" Lesson

>> So let's go write our code that's gonna connect to our client. So here's inside of frontend, we're gonna go into http2-chat.js. And this is a bit trickier. We're gonna have to write a decent amount more code to handle stuff coming over a long running connection as opposed to just a complete connection.

And again, this isn't necessarily what HTTP/2 push was built for. We're kind of abusing a little bit, which is fun. So we're gonna replace the code here in getNewMsgs. We're gonna say let reader. I'm gonna say const utf8Decoder = new TextDecoder("utf-8") is just how this works. It comes encoded, and we have to use this text decoder to decode it into something that we can use.

We're gonna say try here, and we're gonna say const res = await fetch("/msgs"). And then we're gonna say reader = res.body.getReader. So this was new to me. I've never used getReader before, but basically, what this is gonna turn reader into is a readable text stream, right? Which is basically saying, hey, I know there's gonna be a body there and I know it's incomplete.

So give me something that I can continually get stuff back from, and that's what reader is. Okay, we'll put our catch here, catch (e). And we're gonna say console.log("connection error", e). Okay, if we get to this point, then guess what? We have successfully connected to the server. So you wanna say presence, which you see up here I grabbed, which is just that present indicated with the red dot at the top right, presence.innerText.

You can see here very fancy front-end stuff going on here. And we're just gonna set that equal to green circle or whatever you wanna put there. On Mac, that's Ctrl+Cmd+Space, and that'll bring up this little emoji picker. I don't remember what it is on Windows. There is an emoji picker, I just can't remember what it is.

But, I'm sure you could just google green circle emoji and copy and paste it off of Google, too. Or you can just say connected, and that would work, too. Okay, so that'll let us know at the top right that we are connected. And down here again we're gonna say try reader, Where did I put that?

Okay, we're gonna say outside of here, Let readerResponse, like that. I'm gonna say readerResponse is assigned await So basically, what this is gonna say is gonna say, hey, wait here until the API sends me something new back, right? And then we're gonna say const chunk = utf8Decoder.decode(readerResponse.value), and we're gonna say this is a stream.

So this is gonna pass us into our text decoder that we had up here, whatever it got back from the API. And we just need to let the UTF8 decoder let it know that this is not a complete thing, it's a stream. So expect that. And then here we'll just say console.log(chunk).

And then again, we'll say catch (e). We'll say console.error, reader fail. We'll say our presence indicator, we'll have to set that to read. And then we'll return after that. Cool, so what is this gonna do? We're gonna call getNewMsgs, we set up a decoder, we fetch from our API.

And then we say, cool, we know this is gonna be a stream of data coming in, not just one, right? That's why we can't use rest.json here. Rest.json's like, I'm going to wait for the entire thing to finish, which it never finishes, right? By design, our API is never going to finish.

So that's why we have to get this reader which is gonna be a stream of data coming in, right? We set our innerText to be green, we're connected. And then here, we're gonna read just the first response right now. So this is actually still not gonna handle all the requests coming in.

Right now we're just gonna read the first one, and that's it so far. After that, it's gonna finish. But yeah, we'll grab the chunk out of there and then we'll log it, and then we'll get to actually rendering that here in just a second. I'm not making you do the post again, by the way.

We did it for the polling section, and it's the same thing. So the thing to note about HTTP/2 is it's unidirectional, at least in the way that we're using it. That data is going to stream from the server to the client, but it doesn't go in the other way.

All right, so I can't push things on the same socket back to the server. That would be a WebSocket, which we're about to talk about. There is actually a proposal, or actually, I think it's called an extension of the spec that allows bidirectional communication. But it's not gonna work in this particular case.

Okay, so coming back here and refresh and we look at our console. You can see down here in the console, I am actually getting my message from the server, the first one, right? I don't think it's actually sending any more than one at the moment. So this would be it anyway.

But what's cool, if you refresh and look at this again. Look at that there's a 200 here, a 200 here, and there's no number here next to the msgs, messages endpoint. Why is that? Well, according to the browser, it's still an open connection, it still hasn't finished. So therefore, that's why we're not getting a status.

It could still 500 at some point, right? At which point it would show that, but yeah. The dev tools don't really know what to do with this because it's actually still a long running request. Like I told you, we're kind of abusing the system a little bit for fun and profit.

Okay, so this reads once. We want it to continue reading, right? We don't want it to just end. We want it to actually continue trying to read from the API as long as the API is still sending information. So let's do that. What we're gonna do here is we're actually gonna wrap this in a do loop.

We all love do loops, right? When is the last time you wrote a do loop? I don't know. I think, honestly, I think the last time I wrote a do loop I was teaching another Frontend Masters course. I almost never use them. You could totally do this with a while loop.

But any time that I see a place where a do loop fits in, I just get really excited. It's like I'm gonna do a do loop. So we're gonna say do while we're not done. We're gonna create a thing called done. And as long as it's not done, it's gonna just continue running this function over and over again.

In theory, this will actually never complete. So you actually really could put a while true loop here and it would be equally valid. And we're also gonna create a let done up here as well. Okay, so here we get the readerResponse. That's gonna come from the read. The catch, That's fine.

We're actually gonna move this stuff all outside of the try catch down here. You could leave it up there, it would probably be fine. But we'll stop logging. We don't need to do that. And we're gonna say done is assigned readerResponse.done. So I mean, the reason why I did it this way is eventually, in theory, that connection could be closed by the server, right?

In which case this done would come back as true. In which case we'd want to stop listening to the server. We're not gonna write any code that does that, but that's how you would handle, is like, okay, eventually, this will close, right? Yeah, we're not gonna care about that right now.

Okay, and then we're gonna say if (chunk), cuz sometimes you will get back empty frames of data. So you just wanna check to make sure that there's stuff in here. We're gonna say cons, or sorry, we're gonna do another try here. Lots of places to catch errors here.

This is probably more try catches than ever, right? But any time that you do JSON.parse, you just gotta try catch it, because if anything is out of whack at all, it's gonna throw an error. So const json = JSON.parse(chunk). And hopefully, this will be our messages back from our server.

Then we're gonna say allChat is assigned json.msg, and we'll call render. Otherwise, we'll just say down here we'll have a parser console.error("parse error", e). Okay, and then down here underneath the while done, we'll just copy and paste this one as well. If for some reason this while loop finished, right, then you would be disconnected as well.

So you'll put that innerText down there as well. Okay, So again, this do loop should go on forever, right? It should not finish as long as it's open and connected. So if we refresh the page, first thing I want you to notice is whenever you refresh the page, this will go from red to green immediately, which is kinda cool, right?

So now you know that you are definitely connected to the server. Where's my, down here, you can see here, the connecting and disconnecting whenever I refresh the page. Let's see if I can get it to reuse one. Yeah, so far it hasn't reused a number, but you will find that occasionally, it will, It'll reuse some of those stream IDs.

Learn Straight from the Experts Who Shape the Modern Web

  • In-depth Courses
  • Industry Leading Experts
  • Learning Paths
  • Live Interactive Workshops
Get Unlimited Access Now