Complete Intro to Real-Time Polling with requestAnimationFrame
Transcript from the "Polling with requestAnimationFrame" Lesson
>> So there exists a problem with our code. Well hold on, we're gonna continue with that same exercise, right? So we're actually just gonna modify the code that we have for the next part. So you don't have to jump into anything else just so you know, okay. So there exists a problem with our code.
[00:00:20] If you go back and look at our browser window here You'll notice that even though we've been gone, right? It's actually still pulling behind the scenes, which is kind of not ideal for a couple of reasons. One, let's say that I'm working on something here and chatting with someone.
[00:00:41] And then I change the window to this one or something like that. And then I'm gone for two days, right? As long as my computer up and Firefox, hasn't paused to the entire tab, it's gonna keep pulling, right? So this window's gonna be totally up to date. It's gonna be, getting all the new chat stuff and rolling in, but me the user, I'm not seeing any of it.
[00:01:02] So who cares, right? So I'm wasting my server resources by forcing the client to continue to pull and I'm also wasting that users CPU cycles, memory and then data, right? And I mean you can imagine this could be something like slack or discord or something like that where they actually have a lot of data going back and forth that could actually be pretty bad for a user's data cap.
[00:01:29] So, how can we fix that? How can we make it pause on focus? Well, there's something called requestAnimationFrame, which is actually one of my favorite tools that the browser provides you. We're not gonna be doing any animations today. But you can actually use requestAnimationFrame which has just a bunch of stuff built into it.
[00:01:53] One of the things that it has built into it is that when a user unfocuses the window, it pauses requestAnimationFrame, which is great. So anything that you want to just pause when a user is no longer paying attention to it, you can just say, hey, use requestAnimationFrame and then the browser takes care of all this stuff for you.
[00:02:17] So set timeout would be good if we definitely didn't wanna pause something. Which I'm trying to think of something like let's say that you have like a script that you wrote that you're trying to find like a PS5 or something like that, right? Something that's difficult to find.
[00:02:33] You wouldn't want that to pause in the background, right? Because if it found something you'd want it to immediately just say, hey, I found it, right? You need to go buy it right now, or concert tickets or something like that. So that would be a great use case for set timeout.
[00:02:45] But for something like maybe checking the weather, you don't need to have up to the date second weather updates, if the user is not paying attention and reading what's coming back from the weather API, right? So those are kind of two different reasons why you might use set timeout versus requestAnimationFrame.
[00:03:05] Okay, so let's go re implement this in terms of set animation frame. Sorry, requestAnimationFrame. So come back here to get new messages. We're gonna delete the set timeout line here. And we're just gonna leave this like this, right? So now this function has no concept of when it's gonna be called again, we're gonna have something that's gonna call it rather.
[00:03:37] Okay and then come down here to the bottom and we're gonna replace this here. This line 61 get new messages. So we're gonna say let time to make next request equals 0. And we're gonna say async function rafTimer, that's what I call that. There's probably a better name for requestAnimationFrame.
[00:04:07] And this accepts a time. And you're gonna say if, time to make next request is less than or equal to time, time being what is provided back from requestAnimationFrame then await get new messages And then after it has finished making that request, as soon as this function has completed you're gonna say, timeToMakeNextRequest is equal time + INTERVAL.
[00:04:55] And then down here you'll say requestAnimationFrame rafTimer. So, actually already see a problem with this. We'll talk about that in a second. What this function is do is it's gonna be called by requestAnimationFrame, right? And that's gonna be given a time, right? It's like basically what time is it right now when I'm calling this.
[00:05:23] We're gonna have some concept of when the time to make the next request is right? And if that time is smaller than the current time, the time to make that request has passed. And then we're gonna call that and we're just going to say, wait for this message to come back.
[00:05:39] And then once you do, then decide when the time to make the next request is and then you'll queue it again basically. Down here we'll also have to call requestAnimationFrame rafTimer. This'll kick it off for the first time, right? Which is what we need to do. So the thing to note about requestAnimationFrame, like pulling this function gets called a ton.
[00:06:07] Basically every time that the browser is idle, right? Where it's not doing anything else, it's gonna say, cool, I have time to kick off a request for requestAnimationFrame. So what's also nice about this is this won't interrupt your browser whereas like set timeout, as soon as like that timer finishes, it's a hard halt on anything else that was doing it's like all right, it is definitely time now.
[00:06:30] I was told to time out in three seconds. It's timed now, and it will pause everything else. So if you're doing a ton of animations on your screen. Or I don't know, something that would cause jank like streaming video would be another good example of this. It will hard halt everything.
[00:06:47] Which can cause jank in your UI, right? Particularly if you're dealing with a lot. The nice thing with requestAnimationFrame, it waits for the browser to say, cool, I'm tided up for my responsibilities for the moment. Give me something else to do. That actually happens a lot. It will happen thousands of times a second, right?
[00:07:05] So this function gets called a ton. So this needs to be pretty bare bones, right? Don't do a bunch of analytics, ingesting during this, you want it to be basically is the time to run yet yes or no okay and then I'm done and I'm gonna cue myself to run again, so it runs really fast.
[00:07:25] It sounds inefficient, it actually works out really well. Because again, remember this was built for animations. And those require like a lot of overhead. The thing that I realized here while I was writing this, that's actually kind of problematic. Is if this get new messages takes a long time, this time can actually be kind of behind, right?
[00:07:49] Because this will be determined when you call it for the first time so I actually think you need to see Date.now. We'll see if this works. So as a danger of writing code on the fly, but because you want the time to be from when this has already finished right, this could take 20 minutes, right?
[00:08:08] We don't want it to run immediately. We want it to schedule three seconds further out the future. Okay, let's see how that works. So we're gonna refresh the page over here. Again, notice that it will have dropped all of your, Okay, that's fine. Your message is cuz we saved the server.
[00:08:50] Yeah, this is what I was afraid, all right, let's try this with time instead. Okay, so that is working now. So I know you'd have to go figure out what I did, but it's basically just messing with the time to make sure that this is correct. My guess is ones in milliseconds and one is it's not or something like that.
[00:09:16] So if we look down here at our network tab, notice this is still pulling about every three seconds, right? And actually probably pretty close to exactly every three seconds. The nice thing about this is if I go hop into another window which you can go test it on your own.
[00:09:36] But if I unfocuses then focus on something else, it'll keep running for a little bit. But after a little bit, the browser is gonna stop calling requestAnimationFrame and it's just gonna stop. So I wonder even if they came over here and say come in this window and say, Let's admit that it's not gonna pause this other tab immediately.
[00:10:04] So if I went over there now it probably actually would have made that request, yeah, which it did. But eventually over time it would stop calling requestAnimationFrame. And it would pause that and then we would come back, it will immediately queue that requestAnimationFrame to run again, so it'll actually unpause it for you.
[00:10:21] So it's pretty seamless, the browser will do a lot of that heavy lifting for you. What is the time parameter? I mean, let's just log it. Some old timey login. I don't know why it's old timey. It just seemed funny to me when I said it. So you can see here, yeah, you can see there's actually very different than date.now.
[00:10:42] I think it's milliseconds since it started running. So you can see how frequently this is getting run. It's getting run a lot. So I would say every. 100 milliseconds, something like that, not 100 milliseconds, rather like every 10 milliseconds this is getting run. Tme is hard anyway, it gets a lot.
[00:11:15] That's my point. So yeah, it's just a time that the browser's giving back. Yeah, I think this is milliseconds since the current browser session has started. And then to answer your question, we're not providing this to get new messages here because getting new messages actually doesn't care, right?
[00:11:41] It doesn't use the time anywhere inside of here, so it doesn't need to know anything about it. Again, what's nice about this, let's say I'm in a different part of my application and I need to call that endpoint for a different reason. I don't have to give it a fake time, right?
[00:11:54] It can just be called so this function is useful outside of just being a polling function as well.
>> Shouldn't time to make new requests be initialized with date.now?
>> Good question line 60 here, actually that's a really good question. Why, do I initialise this to zero? First of all time here, as we just explained, which I'm sure that person had asked that previous to that is actually second since the start.
[00:12:22] So this is equal to zero when the browser starts, right? And whenever we start this, we want it to run immediately the first time, right? So whenever the page loads for the first time, I wanted to immediately request all the messages just from the API. So I set that to 0, so that I'm guaranteed whenever this runs for the first time, time it's gonna execute this line 63 for the first time, right?
[00:12:54] So everything that you and I just did together is in the pause on unfocus directory here. So if you wanna go back and reference that, or you miss something that you wanted to go see, that's the state of what we've coded up so far. We'll be here on the pause on unfocused part of the repo.
[00:13:16] Before we get into back off and retry, anyone have any questions that they wanna ask about requestAnimationFrame? Just general coding advice from me if you're working in browser stuff, requestAnimationFrame I think is one misnamed. It should be called like schedule in the future because it's useful outside of animations as you can see here, I use it for a lot of stuff.
[00:13:43] This is definitely not the first time I've used it in the course I can't remember which one other ones I've done it, but suffice to say very useful and yeah, it's it's awesome. You can use it for lots of stuff.