Check out a free preview of the full Advanced Asynchronous JavaScript course
The "Error Handling" Lesson is part of the full, Advanced Asynchronous JavaScript course featured in this preview video. Here's what you'd learn in this lesson:
Inspired by a student suggestion, Jafar reviews error handling with observables.
Transcript from the "Error Handling" Lesson
[00:00:00]
>> Speaker 1: What about errors?
>> Jafar Husain: In this context, I think it's actually a good opportunity to talk about error handling.
>> Speaker 1: So if any observable throws and error, doesn't that kill the whole-
>> Jafar Husain: It would. Is that what we want in this case, you think? No, not really, right?
[00:00:14]
I mean the platform errors, maybe it's a big deal, but that's not really our problem, at least, the problem of this routine, that's supposed to keep track of whether animations are allowed or not. So I think it's a great thing to bring up. How would we, effectively, ignore an error?
[00:00:31]
So in this case, I would just basically ignore an error, and treat it like a complete.
>> Jafar Husain: Does anybody know about any of the operators we have with observable for handling errors? There's a couple ones that are super common. One is catch. So let's say I'm gonna denote in observable the errors like this.
[00:00:55]
In catch,
>> Jafar Husain: And that'll give me this.
>> Jafar Husain: Almost, I got that almost right. So catch accept accepts a function which accepts the error that occurred, and it returns a new observable to resume from this observable. And that has effectively, it's a little like a concat right? Cuz you're basically in the event of an error instead of allowing the error to propagate through, you're concatenating on some observable in place of that error and continuing on from that observable.
[00:01:40]
Does that make sense,
>> Jafar Husain: Sorta? So let's see if we can apply that to this problem. So we know that tasks, that any of these individual tasks could error, right? So what do we wanna do in the event any of these tasks error? So specifically this task right here, any of these could on error.
[00:02:04]
What would we have to do?
>> Speaker 1: Return an empty observable.
>> Jafar Husain: Return an empty observable, so.
>> Speaker 1: From the catch.
>> Jafar Husain: From a catch, yeah. I don't care about the error, I'm just gonna return an observable.empty and that's basically how you ignore errors. Not that I'm advocating for that all the time, but in this particular context it's a good idea.
[00:02:23]
And that's how you ignore errors with RX. So this is the equivalent of,
>> Jafar Husain: An irregular function. Does that make sense?
>> Jafar Husain: Can anybody think of something else I might do? In this context I wouldn't do this, but can anybody think of another way I can handle an error in an observable?
[00:02:50]
It's very common thing on the web to handle an error this way.
>> Jafar Husain: Let's say I make a network request is gonna come up in the next example. Let's say I make a network request and it fails. Generally speaking what do I want to do? Cuz it's the web and stuff happens.
[00:03:07]
>> Speaker 1: Retry.
>> Jafar Husain: Yeah I don't wanna just give up, explain our message just yet. We know that servers even Netflix only gets like three nines of resiliency so every now and then a request times out. So as a result we just wanna retry a couple times, so we also have retry.
[00:03:27]
>> Jafar Husain: And then you can put in the number of times you wanna retry. So basically, what this is gonna do, is it's gonna retry, which means that, when it fails, it's gonna resubscribe to the original observable, and now we've come full circle. Let's talk about cold and hot observables again, and lazy and eager observables.
[00:03:44]
Remember, we talked about the idea, of the fact that every time you subscribe to an observable, depending on how you've written that observable, you could kick off another network request. Well, this is where it pays off. Retry, just all it does it subscribes if it gets an error, it just resubscribes to the same observable.
[00:04:03]
And in this case, it's absolutely you want to kick off another network request. So this is another area where being lazy is more powerful, because instead of doing something you've created a function which can do something when you call it. You can call that function multiple times, and that's how you retry.
[00:04:20]
So if you get a promise, you can't retry a promise, you have to go find whatever function created that promise. Which who knows, could be 17 functions away in your program and then you need to find it and you need to create another promise. Not so with an observable, so a great example of where laziness pays off.
[00:04:38]
So, do I have to implement retry or does everybody kind of get an idea of how that would work, how retry works. Happy to implement it. Would we prefer that? Would we wanna take a look at it? Quick show of hands? Yeah, good, great, let's try retry. It's a fun little one to write, actually.
[00:05:00]
>> Jafar Husain: And where did I put that? Here we go. Nope.
>> Jafar Husain: So let's throw in our retry operator.
>> Jafar Husain: So help me out with this, guys. What's retry gonna return?
>> Speaker 1: New observable.
>> Jafar Husain: [LAUGH] Good default answer, I love it. What's going to happen when you subscribe to this observable?
[00:05:33]
>> Jafar Husain: So, when we call retry, we create a new observable, that when subscribed to, will subscribe to the source. And if it gets an error from the source, we'll re-subscribe to that source a certain number of times. After which if it continues to get an error, it will give up and forward the error downstream.
[00:05:51]
So with that verbal description, what am I gonna do here? It's a little bit like Map cuz we need to subscribe to a source. So what's the first thing we gonna do, cuz we're in JavaScript and it's maybe not the world's best programming language.
>> Speaker 3: Set self equal to this?
[00:06:05]
>> Jafar Husain: Nice.
>> Speaker 3: We need to have a handle to the source observable i.e this, so that we can pull data out of it. So now that we've done that, how are we gonna get data out of our source?
>> Jafar Husain: Self.subscribe?
>> Speaker 3: Self.subscribe
>> Jafar Husain: Great, so if we get data, what do we do with it?
[00:06:28]
>> Speaker 1: Pass it along.
>> Jafar Husain: Pass it right along.
>> Jafar Husain: Just gonna save this here.
>> Jafar Husain: And if we get a complete message, what are we gonna do?
>> Speaker 3: Complete.
>> Jafar Husain: Everybody's happy, right? If the sourceObservable completes we're done-zo. But what if we get an error?
>> Speaker 3: Retry and null minus 1.
[00:06:52]
>> Jafar Husain: Retry, right? Okay so, and how am I gonna retry? Cuz I could take this whole thing and I could paste it right here. That's one way of retrying, right? That when I get the error message, I just re subscribe to self. What's a better way than copying and pasting code?
[00:07:11]
>> Speaker 3: Recursion.
>> Jafar Husain: Recursion, yes, for the win. All right, so we're going to do the same thing we did earlier with our concat, we're gonna make a function called processRequest, there's no need for that not to be a const.
>> Jafar Husain: Cool and now in here we'll call a processRequest and then we try all over again, right?
[00:07:52]
>> Jafar Husain: Okay, what's not gonna work about this?
>> Speaker 1: Should it be static?
>> Jafar Husain: Should it be static?
>> Speaker 1: Yeah.
>> Jafar Husain: The processRequest function?
>> Speaker 1: The whole retry.
>> Jafar Husain: Well, you want to retry an individual observable. So in this case it wouldn't be static, because let's say I just created an observable that represents a network request.
[00:08:13]
And then I just wanna go .retry and now I have an observable that will make that network request n times if there's a failure. So in this case it makes sense for it to be a instance method. But I think we've picked the right thing here, but as it is I don't think we're done.
[00:08:30]
I mean I haven't returned the subscription.
>> Speaker 4: It'll also go infinitely.
>> Jafar Husain: It'll also do it infinitely if there's an error, right? That seems like a problem. And how do I know when to stop?
>> Speaker 1: Can we at this time pass in the value to the recursive function?
[00:08:47]
>> Jafar Husain: Yeah, we can. I see where you're going with this. So I have num, if I pass num in there then when I call processRequest recursively, maybe I shouldn't mask that variable, currentAttemptNumber.
>> Jafar Husain: So for starters, I have to actually run this function otherwise nothing's gonna happen, so I'm just gonna call it once.
[00:09:10]
And at first I'm gonna pass it in the same number of retries that was requested. So after I've gotten one error, what number am I gonna pass in here?
>> Speaker 1: We decrement it.
>> Jafar Husain: Decrement it? More specifically I will do this. And so how do I know when I'm done?
[00:09:37]
>> Speaker 1: We check at the top. If it is the two negative 1 would be, or is it 1.
>> Jafar Husain: I think 0 in this case cuz it's gonna be, I could be wrong, who knows, off by one error if it happens. And yeah, question?
>> Speaker 1: I have a question.
[00:09:55]
Shouldn't it be static if it is creating an observable?
>> Jafar Husain: No, because Map creates an observable, it's not static, right? Filter creates an observable, it's not static. It's a good question. Why are some functions static and some functions not static? In general, the functions that are static are the ones that are creating an observable from nothing or at least, from a couple of parameters.
[00:10:18]
But once you already have an observable, and you are transforming that observable that's generally when you want to use an instance method. So you can go, my observable dot some operation, dot some operation, etc. So as we can see here in this static function timeout we didn't have an observable, all we had was a time at which we wanted to create an observable that would fire after that time.
[00:10:40]
And so that's why we have a static function. But in the case of Map what we're saying is I have an observable with some data inside, I want to transform all of the data in that observable into a new stream. And so Map takes Map as an instance method on an observable, that creates a new observable.
[00:10:56]
So that's why it's an instance method. Likewise with retry, I have some observable that's gonna give me a stream. But if that stream should error, I might wanna retry subscribing to that stream, and that's why retry is an instance method on observable. But it's a great question, so it's worth clarifying.
[00:11:15]
Every recursion has the recursive case, the case where we're repeating, and that's what we've handled right here.
>> Jafar Husain: And it has a base case, the case where we wanna stop. We always have to stop at some point, otherwise that's no fun, right? So what do we do here when we stop?
[00:11:35]
>> Speaker 1: Complete?
>> Jafar Husain: Are we going to complete? So the way retry works is if it gets an error, it tries to resubscribe, but after a while, if it keeps getting an error, then instead of completing it actually forwards the error along. So in this case, I would assume that I would do something else.
[00:11:51]
What would I do?
>> Speaker 1: Error.
>> Jafar Husain: Observer.error, absolutely. And, no, where's my error object? Did I lose it? What happened?
>> Speaker 1: It's inside of the subscribe, self.subscribe.
>> Jafar Husain: Right, it was all the way down here. We called processRequest but our little old error got lost, how are we going to keep track of it?
[00:12:17]
>> Speaker 5: We could put the logic check in the error.
>> Jafar Husain: Cool, I see where you're going here. We could do that. What is somebody puts in retry 0? Is that guy just a jerk? Should he not do that? Screw that guy, let's not worry about him, let's put it in here.
[00:12:38]
I think there might be a tidier way of doing this but for now let's put this in here. So, if (currentAttemptNumber === 0) then.
>> Jafar Husain: I like this better. I think you're right. It's nice
>> Jafar Husain: So here we will error.
>> Jafar Husain: Cool, does that make sense? Am I done?
[00:13:22]
>> Speaker 1: Did we return the subscription?
>> Jafar Husain: No, we did not.
>> Speaker 1: Or the other No, we didn't return that subscription.
>> Jafar Husain: So I guess I'll just return the subscription. Wait, where is my subscription?
>> Speaker 1: We can do the same thing like we did in concat and keep track of it.
[00:13:37]
>> Jafar Husain: Yeah, we could create a reference to whatever the current subscription is
>> Jafar Husain: And just keep, whenever we subscribe, again we could just reassign it. And then we can return a subscription object,
>> Jafar Husain: That just takes whatever the current subscription is
>> Jafar Husain: And then subscribes when it's called.
[00:14:08]
>> Jafar Husain: Probably have to null check that.
>> Jafar Husain: I might not have to, actually. I don't think I do.
Learn Straight from the Experts Who Shape the Modern Web
- In-depth Courses
- Industry Leading Experts
- Learning Paths
- Live Interactive Workshops