Error Handling with Observables

Ғылым және технология

When we issue an http request to get or update data, there is always the chance that something will go wrong! The item the user requested isn't found, the updated data doesn't pass the server-side validation, our boss hasn't paid the electric bill and our server is down ...
We look at several scenarios: Catch and Rethrow, Catch and Continue, Retry on Error, and Action Streams
Links
Sample code: github.com/DeborahK/Angular-P...
Content
00:00 Why error handling?
00:24 Options for handling http errors
00:59 Catch and Rethrow (service)
01:54 Observable of never
03:30 Catch and Rethrow (component)
03:44 EMPTY
03:57 Result of Catch and Rethrow
04:04 Catch and Continue (service)
06:06 Getting a message to the template
06:35 Result of Catch and Continue
06:44 Retry on Error
07:27 RetryWhen
08:24 Action streams
09:54 Wrapping up
▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
😊About Me
Hey! I'm Deborah Kurata
I'm a software developer and KZread content creator. I speak at conferences such as VS Live and ng-conf. I write articles for freeCodeCamp. And I'm a Pluralsight author with courses in the top 10 most popular (out of 10,000+) over the past 5 years. For my work in support of software developers, I've been recognized with the Microsoft Most Valuable Professional (MVP) award, and I'm a Google Developer Expert (GDE).
Contact me on Twitter: / deborahkurata
Find my Pluralsight courses: www.pluralsight.com/profile/a...
Access my freeCodeCamp articles: www.freecodecamp.org/news/aut...
View my KZread content: / @deborah_kurata
▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
#angular #angulartutorial #bestpractices #demo #errorhandling #exceptionhandling #rxjsangular #angularrxjs #catcherror

Пікірлер: 56

  • @davidcooper4327
    @davidcooper4327Ай бұрын

    I'm new and am building a few little apps to learn. Every video I've watched of yours in the last few days has covered a specific issue I'd noticed but couldn't find a satisfying answer for. The observable thing is the latest such example. Thanks a bunch for these videos.

  • @deborah_kurata

    @deborah_kurata

    Ай бұрын

    So glad to hear that they have been helpful. Thanks!

  • @metric152
    @metric152 Жыл бұрын

    I didn’t know about the retry when operator. That looks really nice so you can fine-tune how you want it to retry the request. Thanks for the tutorial.

  • @tanishqmanuja

    @tanishqmanuja

    Жыл бұрын

    I also used retryWhen most of the time, but docs say it will be deprecated in the future. They suggest using the retry operator with a config object now. Similar functionality can be achieved.

  • @EvertonCanez
    @EvertonCanez9 ай бұрын

    I love your content, Miss Debora! Thank you so much!

  • @deborah_kurata

    @deborah_kurata

    9 ай бұрын

    Glad you enjoy it! Thank you!

  • @dragonof7720
    @dragonof77202 ай бұрын

    Glorious video! Extremely helpful!

  • @deborah_kurata

    @deborah_kurata

    2 ай бұрын

    Great to hear. Thanks! 😊

  • @looovequran6384
    @looovequran6384 Жыл бұрын

    Thanks a lot ! Very useful and well explained !

  • @deborah_kurata

    @deborah_kurata

    Жыл бұрын

    Thanks!

  • @fieryscorpion
    @fieryscorpion5 ай бұрын

    This was amazing. Thank you so much!

  • @deborah_kurata

    @deborah_kurata

    5 ай бұрын

    Glad it was helpful!

  • @g-luu
    @g-luu Жыл бұрын

    Thank you. Very useful.

  • @user-is5bs8qb4y
    @user-is5bs8qb4y Жыл бұрын

    very well explained! thanks!

  • @deborah_kurata

    @deborah_kurata

    Жыл бұрын

    Thank you!

  • @paulven5467
    @paulven54678 ай бұрын

    Very useful, thank you

  • @deborah_kurata

    @deborah_kurata

    8 ай бұрын

    Glad it was helpful!

  • @DebugModeVideos
    @DebugModeVideos Жыл бұрын

    Thanks

  • @deborah_kurata

    @deborah_kurata

    Жыл бұрын

    Hey! Great to see you here. Thank you for posting!

  • @TheZukkino
    @TheZukkino10 ай бұрын

    what about using an interface Response as follow? interface Response{ data: T, message?: string } or something similar? Great video, really interesting, important and clear for newbie like me approaching Angular

  • @deborah_kurata

    @deborah_kurata

    9 ай бұрын

    Yep. If you look at this one: stackblitz.com/edit/angular-signals-rxjs-deborah-wrapper ... that is what I did.

  • @avrimalka8798
    @avrimalka87987 ай бұрын

    Hey, thanks alot for the video, it is great!! I have a question: You mentioned that type of Observable represents an observable that neither send 'next' nor 'complete' notifications, however, according to rx js, EMPTY is also type of Observable which is an observable that completes only. Perhaps type of Observable represents an observable that might emit 'complete ' or 'error' notification only? What do you think?

  • @deborah_kurata

    @deborah_kurata

    6 ай бұрын

    Thank you. Glad the video was useful. Here is the docs for never: "A simple Observable that emits neither values nor errors nor the completion notification. It can be used for testing purposes or for composing with other Observables." The source code for NEVER looks like this: export const NEVER = new Observable(noop); Using observable prevents completing a stream or emitting anything from it. It's used when you throw exceptions because an errored stream can no longer emit a complete or next notification. And for EMPTY: "A simple Observable that emits no items to the Observer and immediately emits a complete notification." The source code for EMPTY looks like this: export const EMPTY = new Observable((subscriber) => subscriber.complete()); Does that answer your question?

  • @avrimalka8798

    @avrimalka8798

    6 ай бұрын

    @@deborah_kurata Thanks a lot. I am sorry but I did not explained my self well. I refered to the :Observable, which confused me a little. From the video, at 1:54, there was the explanation about : Observable which represents an obserable which emits neither 'next' nor 'complete' events and might error. Then I looked at rxjs doc about the value of EMPTY, which complete only ,and I saw that it also has the type of Observable, thus, I got a bit confused. Is the type of Observable refers to Observable that might only error/ complete? of course the values NEVER and EMPTY have their functionalty as you mendtioned. What do you think?

  • @deborah_kurata

    @deborah_kurata

    6 ай бұрын

    @@avrimalka8798 Using observable prevents completing a stream or emitting anything from it. It's used when you throw exceptions because an errored stream can no longer emit a complete or next notification.

  • @avrimalka8798

    @avrimalka8798

    6 ай бұрын

    @@deborah_kurata observable could still complete, for instance: function foo(): observable { return EMTPY; } I talk about the return type, which could also includes EMPTY in this case.

  • @deborah_kurata

    @deborah_kurata

    6 ай бұрын

    @@avrimalka8798 You are correct, I don't understand the question. 🙂

  • @carolruo
    @carolruo Жыл бұрын

    Nice

  • @deborah_kurata

    @deborah_kurata

    Жыл бұрын

    Thanks! 🙏

  • @bongguiao
    @bongguiao Жыл бұрын

    Hi Deborah, another very useful video, how about convert it to signal?

  • @deborah_kurata

    @deborah_kurata

    Жыл бұрын

    Thank you! I've been doing some research on error handling with signals. You can find my many attempts here: stackblitz.com/@DeborahK/collections/angular-signal-error-handling-research

  • @ericgichimu3358
    @ericgichimu3358 Жыл бұрын

    Wow!

  • @deborah_kurata

    @deborah_kurata

    Жыл бұрын

    Thanks!

  • @Dimitri933444
    @Dimitri933444 Жыл бұрын

    Isn't the retryWhen operator deprecated? You could also do: retry({count: 3, delay: 1000})

  • @deborah_kurata

    @deborah_kurata

    Жыл бұрын

    Yes! I saw that but thought at the time that only one of the signatures was deprecated. But I think you are right! Thanks for the info!

  • @mtvspec
    @mtvspec Жыл бұрын

    Thank you, Deborah! I have a case there user submits a form via http post request and if this request fails stream completes and user can't submit form again. In implementation I used your approach with Subject + asObservable + switchMap. I tried catch + re throw + Observable, this approach help me in a situation, which forces me to transform stream in catch error part with returning another type in error case, and in outer stream in tap I used type guards to handle two different types (for success and error result). Can you help me with scenario in which user can continue to change form data and submit it again even if http error occurs? Can we somehow combine approach with observable `never` and does not re throw error?

  • @deborah_kurata

    @deborah_kurata

    Жыл бұрын

    I'm ooo this week. I'll look at your scenario when i return next week. 😀

  • @deborah_kurata

    @deborah_kurata

    Жыл бұрын

    Could you work up a stackblitz that demonstrates the issue? (Not the entire application, just the code to demonstrate your scenario). That way I can be sure I understand and can provide a more specific suggestion.

  • @nathanalberg
    @nathanalberg Жыл бұрын

    You should rarely have to declare a return type at all with Typescript. TS can infer the type by what you return in the function. In fact.. you should avoid it.. you can muck up your code. throwErrors return type is already Observable

  • @deborah_kurata

    @deborah_kurata

    Жыл бұрын

    As my personal "best practices", I often define a function's return type. That way I can be sure that I'm returning what I'm expecting to return. It's prevented errors for me on numerous occasions. But this is one of those "personal preference" or "team preference" things. :-)

  • @imjmakki
    @imjmakki Жыл бұрын

    hope you're gonna making video about api and how it been consumed from backend specially with @Input and standalone it's a little bit confusing

  • @deborah_kurata

    @deborah_kurata

    Жыл бұрын

    Thank you for the suggestion. To ensure I'm clear, the video should cover: issuing an http.get request to a backend api and use the result in a child component using @input?

  • @imjmakki

    @imjmakki

    Жыл бұрын

    @@deborah_kurata Great in that way I can use it inside the interface right?? such as inside user interface??

  • @dg-cg5gi
    @dg-cg5gi11 ай бұрын

    Hey Deborah, I've been testing out the catch and continue and it's really slick, but can you give the code for your HTML how you are passing it along to the HTML? I have it written very identical with the wrapper class in my service call and when logged to the console it grabs the error! Amazing! But how do I transfer that to my component and then to my HTML? All that's done in my service class, but in my component I actually have another variable like grabdata$ = this.dataservice.postCall$.pipe( map(res => res.data.objectArray) So my service it's catching the error with that wrapper, but In my component I'm actually map data down to get specific data, so how do I pass that error to my component or my HTML? I didn't see that in the video! Thank you for everything!!

  • @deborah_kurata

    @deborah_kurata

    11 ай бұрын

    You can find the sample code here: github.com/DeborahK/Angular-posts/tree/main/posts-exceptions Check out the folder "single-user-with-message" Service: // Catch and continue with message // If an error occurs, return an empty array of todos // Add a wrapper for a message to the UI todosWithMessage$ = this.userSelectedAction$.pipe( switchMap((userId) => this.http.get(`${this.userUrl}/${userId}/todos`).pipe( map(todos => ({ todos, message: '' } as ToDoData)), catchError(err => of({ todos: [], message: `Error retrieving todos for user` } as ToDoData)) )) ); Component: errorMessage = ''; todosWithMessage$ = this.userService.todosWithMessage$.pipe( // Do any other mapping here // Assign the message to the errorMessage property tap(todoData => this.errorMessage = todoData.message), catchError(err => { this.errorMessage = err; return EMPTY; }) ); HTML: {{errorMessage}} Would that work?

  • @dg-cg5gi

    @dg-cg5gi

    11 ай бұрын

    It's not working how I am thinking it should.. Here's my code: please Ignore variables as I had to change them service: postReq$: Observable = this.baseFormAction$ .pipe( switchMap((form) => { let body: RequestObject= this.buildPayload(form); this.baseEvent(new setEvent({}, false)); return this.http.post(this.PCFUrl + "/sets", body).pipe( catchError(err => of({ data: this.getNullResponse(), errors: err.error } as ResponseObject)), tap(response => console.log(response)) ); }) ); so if there's an error then we are passing a new observable of that type but with an error property on our main object. Our component (we need to map the data to an inner object array like we do): setResponse$ = this.dataService.postReq$.pipe( map(res => { if (res.errors) { this.errorMessage = res.errors; } return res.data.otherData?.setsArray ?? []; }), catchError(err => { this.errorMessage = err; console.log(this.errorMessage); return EMPTY; }) ); But I notice that whenever we get an error in our service, it doesn't actually get handled in the component so the catchError operator is redundant and is never triggered. However, because our inputs are completely reactive, the errorMessage doesn't ever go away. I tried using an Observable but that also never went away after calling next on it so. It technically works like this - just doesn't seem very clean.

  • @deborah_kurata

    @deborah_kurata

    11 ай бұрын

    @@dg-cg5gi Yes, the `catchError()` in the component should never get triggered because the error is caught in the service. The error is part of your object, so in the component the code in the `map()` should handle it. When you say it never goes away, do you mean if you reget the data the error is not cleared? In the service, you should clear the error when you get the data successfully. Notice the `map` part of my service example: map(todos => ({ todos, message: '' } as ToDoData)), When the request is successful, it clears the message property of the wrapper object. Does that help?

  • @dg-cg5gi

    @dg-cg5gi

    11 ай бұрын

    @@deborah_kurata Ahh that makes sense! Thank you! I missed that!

  • @dg-cg5gi
    @dg-cg5gi Жыл бұрын

    Hey Deborah, I enjoyed the video, but I believe there is an error here. Atleast, that's what is happening in my code. Returning the EMPTY doesn't actually work because the type, in your case users$, subscribes to that and you aren't ever able to re-do another request. Same thing happened in my code, I have a data stream with an action stream, and with the same error-handling you have, I can do a negative request and then try to do a positive request (without refreshing the page) and it will not do anything because my variable is stuck with the EMPTY constant.

  • @deborah_kurata

    @deborah_kurata

    Жыл бұрын

    Thank you for watching! I was demonstrating several different techniques, so not all of the code demonstrates how to "continue" from an error. You are correct that returning EMPTY does indeed emit a complete notification and won't allow the observable to continue. And any code using the generalized `handleError()` method will error out and not continue. The code that does demonstrate how to continue is the todos$: todos$ = this.userSelectedAction$.pipe( switchMap((userId) => this.http.get(`${this.userUrl}/${userId}/todos`).pipe( // Return an empty array if an error occurs catchError(err => of([])) )) ); And the usersWithTodos$: // Try 3: Catch and continue // If an error occurs, displays data for user with no todos // Will *not* recatch in the UI to display a message usersWithTodos3$ = this.http.get(this.userUrl).pipe( mergeMap(users => forkJoin(users.map(user => this.http.get(`${this.userUrl}/${user.id}/todos`) .pipe( map(todos => ({ user, todos } as UserData)), catchError(err => of({ user, todos: [] } as UserData)))) ))); Notice that in both cases, we return some type of "default" data ... not EMPTY.

  • @dg-cg5gi

    @dg-cg5gi

    Жыл бұрын

    @@deborah_kurata Thank you for that.. I am a bit confused though because in the pluralsight course, we use an errorSubject so that way in the html, it will work async, but how would that work in this case? That's where I'm getting confused.

  • @deborah_kurata

    @deborah_kurata

    Жыл бұрын

    If you catch and continue, the error is caught in the service and returns an empty array to the component. The component doesn't know about any error. So the component wouldn't have a way to react to the error and emit into the errorSubject. If you want to catch and continue but also want to pass the error along, you'll need to create a wrapper object something like this: export interface UserData { user: User; todos: ToDo[]; error: string; } Then in the service, you would add the error information to the emission like this: catchError(err => of({ user, todos: [], error: err } as UserData) Then the component could check the error property. Hope this helps.

  • @osph5525
    @osph5525 Жыл бұрын

    Nice video. But this is RXJS making simple things complicated. I suggest using Axios and signals. Then call it a day.

  • @g-luu

    @g-luu

    Жыл бұрын

    Why would you use axios a 3rd party library when angular has http built in though? And signals are not out yet either.

  • @deborah_kurata

    @deborah_kurata

    Жыл бұрын

    Yeah, some people really like RxJS (such as myself). Others not so much. I came from a .NET background, not JavaScript, so the whole promise thing never really seemed natural to me. RxJS is very similar in a lot of ways to LINQ, so it felt familiar. When we make a meal, we most often make what we know. Same with code ... we often pick technologies/techniques based on our background, what we know, and what feels comfortable for us. Thanks for posting!

Келесі