Skip to main content
771

May 20th, 2024 × #promises#error-handling#javascript

Promises: Error Handling, Aborts, and Helper Methods - Part 2

Discussion on advanced promise concepts like canceling, controlling, helper methods, error handling strategies, and static methods on the Promise constructor.

or
Topic 0 00:00

Transcript

Scott Tolinski

Oh, welcome to Syntax. In this episode, we're gonna be taking a part 2 to our series on promises. And in this video, we're gonna be talking about air handling. We're gonna be talking about aborting a promise, helper methods, and more. So, yeah, we're gonna continue our our talk on promises. And after this one, I think you'll have a a great idea about a little bit more in terms of how to use them beyond just the basics. So my name is Scott Tolinski.

Wes Bos

I promise you're gonna have a good idea. Yeah. Okay.

Topic 1 00:29

Promises for good understanding of promises

Scott Tolinski

Well, I promise you, my name is Scott Talinski, and I'm from Denver. With me, as always, is Wes Bos. What's up, Wes? Hey. Not too much. I promise

Wes Bos

that if you have issues with your promises, you're gonna wanna check out Century because Century is going to tell you when your promises are thrown, maybe when they're you have an uncaught reject in one of your promises that you weren't expecting.

Wes Bos

I can tell you, I, myself, Node used to have this thing called Node used to catch your uncaught promises, and they warned for years that they're gonna turn it off. And I said, yeah. Yeah.

Wes Bos

And then Node day, my servers my server crashed.

Wes Bos

And whenever my server crashes, I get a little Sanity alert, and I get an email about it. And I logged in. I thought, why did my server crash? And I looked at the century error.

Wes Bos

It was a fetch request that I had not been or Node. It wasn't a yeah. It was a fetch request, but it was a post. So I was posting some data to a third party service. That service had an outage.

Wes Bos

And because it was unhandled, I didn't catch the error. Yarn. My entire server crashed, which is unreal that, it could crash an entire server just with a a single, fetch Wes. But that happens, and Sanity was able to tell me exactly what happened, and I fixed it in, like, 3 minutes. So check it out, Sanity Scott I o. Check it out.

Scott Tolinski

Sick.

Topic 2 02:00

Canceling promises

Scott Tolinski

Well, let's get into the 1st section of this episode. We're gonna be talking about canceling promises. So you have a promise. It goes off and, like, I I'm sending, or you're sending me to the store. Right? The promise is that I'll return with the stuff.

Scott Tolinski

At any given point, you can call me up and say, hey, Scott. Come back.

Scott Tolinski

Yeah. Cut it out. Stop doing what you're doing. I found the cheese in the fridge. We don't need any more cheese. Or, it's a good way. You can yeah. You could reject a promise at any time.

Scott Tolinski

So that is entirely up to you.

Scott Tolinski

And you can return with the resolve or reject methods at any time as well.

Topic 3 02:45

Controlling promises

Scott Tolinski

You basically have control over the full process throughout which you're working with your promise.

Wes Bos

Yeah. Inside of a promise, you can like we said in the last episode, you get your resolve, you get your reject methods, and you can do whatever you want with those. And often, what that in includes is you have some logic inside of your promise that says, given these use cases, maybe after I've tried clicking the button 6 times and it doesn't work, then then reject or after. Pretty common is you want to put a 5 second time out on a promise after 5 seconds. If this thing doesn't come back with data or after 5 seconds, if the user hasn't clicked this button, then we need to do something in order to to show them. So you'll catch the reject in that in that case. So that that's an example of when you would want to put the logic inside of a promise.

Topic 4 03:39

Promise resolvers

Wes Bos

Also, we have promised that with resolvers.

Wes Bos

Again, we'll talk about them a little bit more in the next episode. It's called deferred, but, essentially, promise that with resolvers will give you the promise, but it will also give you the resolve and the reject methods outside of the promise instead of inside of the promise callback.

Wes Bos

And that can be handy JS if you want to pass those, like, pretty much, like, succeed and Vercel, functions if you wanna pass them to something else. Like, for example, I have buttons on an example somewhere where if you press, like, the done button, that is resolved. But if you push the cancel button, that is a reject. Often, you could just wire those up directly to an ad event listener, and you're up and running. So that's another way you can cancel them. And then also in fetch land, Wes have this idea called an abort controller.

Wes Bos

And you create an abort controller, and that abort controller itself has what's called a signal. You pass that signal to a fetch request, and then you're able to control that fetch request from wherever you have access to the abort controller method. It's a little bit more complex than simply just rejecting because you do want to also cancel the network Wes, and that will take care of all of that stuff for you JS well as there's a built in signal for simply just doing a time out. Because if you wanna set the time out of a fetch function, you don't have to wrap it or anything. You simply just pass the signal of abort signal Scott time out, 500 milliseconds, and then that thing will reject if there's no response back after 5 seconds.

Topic 5 05:16

When to use abort signals

Scott Tolinski

Why would you reach for an abort signal? Because that's not something I feel like I've ever had to do.

Wes Bos

A good example would be if you have a a type ahead that is sending off fetch requests. So you have a search Bos, you start typing in cool, and then you stop for a second, and it goes, I'm gonna start searching for cool. So it sends a fetch request off to your API.

Wes Bos

And as that fetch request is in flight, you just type I f y f I.

Wes Bos

Okay. Now you've you've sent off a second request to the server that for Coolify. Now you have two fetch requests in flight and it is not guaranteed that they will come back in order that you've done it. And often developers don't realize this because you're working with a local database and on local hosts, and there's no latency at all. But you get into real world conditions.

Wes Bos

Your database is somewhere different than your actual user. There could be real world things. So as you are typing, if you are firing off a second fetch request, you need to make sure that you abort any other requests that are currently still in flight. So that's where you'd wanna use an abort signal.

Scott Tolinski

Thank you. Thank you for that. Yeah. Yeah. Well, let's talk about some of these additional helpers for promises. You may have seen some of these, like, promise Scott all, which promise dot all accepts an array of promises.

Example of abort signals for search

Scott Tolinski

And what it will do is it returns or why I should say the promise itself fulfills once the promises themselves complete.

Scott Tolinski

And it usually gives you essentially an array of the values that come back from those promises.

Scott Tolinski

Now I am curious, Wes, because I know you know more about this than I do.

Scott Tolinski

Promise Scott all settled. What is the difference between promise all and promise all settled?

Wes Bos

So all was initially rolled out when we got promises.

Topic 7 07:10

Promise all vs promise all settled

Wes Bos

And the downside to that is if one of those values rejects, the whole thing is is over. So promise that all takes, like you said, an array of promises.

Wes Bos

It wraps them up into 1 mega promise. Yeah. And it waits for all of the promises to be done. And if one of them takes 1 second and one of them takes 10 seconds, you're gonna be waiting the full 10 seconds before you get all of the data. Right? But if one of those were to reject, you might have 2 successful promises, but there's no way to get that data anymore because it it's rejected, and immediately you go straight to the catch when you're you're chaining. Right? So that's kind of a pain if you do care about the successful ones, but not the one that was was failed. Right? Like, for example, if you're uploading 7 photos, you might want to do promise that all and upload all 7 concurrently all at the same time. However, if 1 of them doesn't upload because it's too large or it's the wrong file type or something like that, The other 6 that may have been uploaded correctly or are are still in process, they're all aborted. Right? You're you're you're out of luck. So all settled will return to you an array of the results regardless of if they were all successful or not. They'll give you an array of 2 types of data. Each item in the array will be will have a status, which is fulfilled or rejected, which is a little annoyed to me that that's it's called fulfilled or rejected. Shouldn't that say resolved or rejected? Maybe not. And then on successful ones, you'll get a value property, which has the data that you're looking for. And then the errored out ones, you'll get a reason property, which will have whatever is passed to the rejected method that you have there. So I would say I don't know. Unless you I wanna abort everything, abandon all ship as soon as one of them breaks, you probably want all settled in most cases. Yeah. And then from that, finally, you mentioned this last show of it being really handy JS if you want to do something after it resolves or rejects, like turn off a loader, then you can use finally, and that will run-in either case.

Wes Bos

Now there is also I can confidently say I've never used either of these. Yes. I'm curious if you ever have either. There's promise dot any and promise dot race.

Topic 8 09:27

Promise helper methods

Wes Bos

Do you wanna guess what those are for unless you've used them yourself? Do you know what the difference is? I you know what? Just looking at these, I would say

Scott Tolinski

that any resolves once any of the promises have resolved.

Scott Tolinski

Mhmm.

Scott Tolinski

But maybe okay. Here's what I'm gonna guess. This is this is just totally off the wall guess for me. I'm gonna say they both return once the 1st promise resolves. I would imagine that maybe any also resolves the other promises where race stops trying to resolve the other promises?

Wes Bos

It's close. So they both you're right in that. They both will resolve as soon as the 1st promise is is uploaded. So maybe you have 2 different APIs, and you're sending data to 2 because you you wanna have, like, double backups. Right? And you only care that something has been sent to one of them. Right? And both of those will resolve as soon as the first one is finished.

Wes Bos

The difference being that promise Scott race will return the 1st settled value, and a settled is a reject or a resolve, whereas promised that any will return the 1st fulfilled value. So any is success only.

Wes Bos

Race will return the rejected or resolved

Scott Tolinski

value. The first Node. Again, I don't know. Like,

Wes Bos

in in what case are you firing off multiple promises? Maybe, like, if you had a promise that was waiting for a click on multiple buttons. Mhmm. So you might have, like, 6 promises that are waiting for clicks on 6 different buttons.

Wes Bos

You might only care as soon as somebody clicks one of those buttons.

Wes Bos

And as soon as somebody clicks one of those buttons, then you wanna continue on with the rest. So although, like, just you could pass in multiple selectors to something like that as well. I don't know. That's a contrived example that

Scott Tolinski

I'm trying to trying to twist into work in here. Well, if you're out there and you're listening to this and saying, guys, I have the perfect use case for this.

Scott Tolinski

Hit us up on YouTube. Drop a comment in the video below this video. I wanna hear what people are using race for specifically, because I have used any, but I've never used race before. And either way, I these aren't things I need to reach for very often.

Topic 9 12:08

Error handling in promises

Scott Tolinski

Let's talk about error handling within promises. Now typically, if you're chaining methods on a promise, you can always add a dot catch method that takes a callback with the error that gets returned there. So you've seen that Wes it has a callback with an e or an error, then you can console log the error. You can throw it to Sanity.

Scott Tolinski

You can do all kinds of stuff with it to ensure that your UI JS accordingly to whatever is happening with that dot catch. Now you can also use a try catch statement to wrap your await inside of Wes you're going to await a promise.

Scott Tolinski

However, one thing that I learned from Wes a little while ago JS that you can mid mix these approaches where you can await a variable result, but still chain a dot catch onto that bad boy. So that way, you don't have to wrap the whole thing inside of a try catch. I don't know why, but try catch feels so obnoxious to me. It pnpm you like, it then reindense my code all by 1. It feels like it takes up a lot of space. That's one of those ones where it's like, I will use the Scott catch method, and I will use await to get the value out of a promise, typically.

Wes Bos

One kind of way that I've been writing a lot of my promises lately is this idea of writing a wrapper function around your promises that will internally run a try catch and will return return from that function a what's called a tuple. And a a tuple is like an array that has a known length and a known type.

Topic 10 13:23

Strategies for async/await error handling

Wes Bos

And back in the day, in express JS and Deno JS land, the way that it worked is your callbacks would often give you the error and the data, and you would either have the error or the data. And that was really nice because you could first check if there was an error, and if there's not, you can continue on with the actual data.

Wes Bos

And that's a little bit tricky because you could do try catch, and then you gotta update variables outside of the scope. That's kind of annoying. You can use the away and dot catch, but if you want the actual error on the next line, then, again, it's it's out of scope. You don't have access to it. You gotta do some weird variable things.

Wes Bos

So with this idea of I call it collect.

Wes Bos

I in my TypeScript course, we create a function called collect and learn how to, like, use generics to type something that is so abstract.

Wes Bos

But there's a really popular library out there called await to JS, and this will just give you a function that you can wrap your promises in. And then it will return to you an array. 1st item being the actual error, 2nd item being the actual data. And that's really nice if you need to first deal with an error before you go on with the rest of your thing. Most commonly being, like, you you try to save an item to a database.

Wes Bos

You get the result back. You wanna first check. Wes there any errors saving that item to the database? If if so, then render an error page. If not, continue on with the rest of the page, maybe render out the user page.

Scott Tolinski

So I've been a big fan of this approach. I have 22 statements on this. 1 Yes. We we should take your collect and turn it into a library, and we can call it collect call. That's good. That's a good joke. Oh. Yeah. That's great. Thank you. Yep. And, second, I you know, just even saying this makes me wonder why, you know, dot catch or even like, it feels like the error should be the first in my logical brain when you're doing a promise. Like, oh, if this fails, take care of the failure first.

Scott Tolinski

Then if it didn't fail, we know it succeeds. Like, logically, the order of that makes way too much sense. I've never used await to JS or any of this stuff before, but I like the way it reads when you look at the code.

Wes Bos

Yeah. I was I always was a big fan of this approach. I think it was I'm trying to wonder, like, who popularized this in Node. Js land? I think some of the even the Node APIs that are callback based were like that.

Wes Bos

It's been so long since I've worked on it, but, yeah, it was always give you the error first, then the data, and that forces you to think, okay.

Wes Bos

Handle the bad case first, and then you have your your happy path. Gotta have your happy path. So, yeah, that's I have a little YouTube video I'll link up here detailing the different async await error handling strategies because not one is better than the other. I use all of them. Sometimes I try catch. Sometimes I catch is good. Often, a mix and match is really good. And then if I'm doing specifically, when I do a lot of, like, Node. Js database work, I'll reach for the collect or await to JS implementation.

Scott Tolinski

Yeah. Yeah. It's it's it is interesting because you like you said, like, you do it different ways. To me, it's one of those as a feeling things because you can typically write most of the stuff you're gonna do with any of these approaches.

Scott Tolinski

And I think over time, you just kind of get feeling for which way you like to do what, and that's that's typically how it feels for me.

Wes Bos

I have a really good promise dot race example. So often when I don't know how people use APIs, I'll just go and get up search and search for that API and see. I'll just add to scroll through 15, 20 different code examples.

Wes Bos

Like, what are people actually using this for? And you know what? This is such a good example of of what it's used for JS that if you want to add a time out to something that has a promise, but that promise doesn't have the option to pass it in. Like, you're using somebody else's API.

Wes Bos

They don't allow you to to pass in a time out. Right? Or you have your own promise based function, but then you have to reimplement the time out functionality in every function.

Wes Bos

And, you know, you have to add a timer to it, and that's annoying. That's one of the nice things about fetch where it's built in, but not everything is a fetch. So promise dot race, what you can do is you can say, const result equals await promise dot race, pass it your original promise function, and pass it a like, a time out function that will throw.

Wes Bos

And then that's beautiful because It makes sense. Either or or not even throw. You could just resolve after one second. And then if if the result is empty, then it it timed out. And if the result is there, then then it worked.

Scott Tolinski

Love that. Hey. Love that.

Wes Bos

Yeah. Oh, I learned a thing or two. Last thing we have here is the capital p promise in JavaScript has 2 static methods on it. So Scott reject and dot resolve.

Wes Bos

And I've always wondered, like, why are those there? You know? Like, what are those 4? And I I realized I had used them a couple times JS if you are returning values from a function that may not be a promise, but you want to still maintain the whole promise API because everything else you're working with is a promise.

Topic 11 19:04

Promise static methods

Wes Bos

So for example, you might have a cache API that either fetches some data and returns to you or it would just pull it up from the cache and return it to you. So by returning promise dot resolve with some data, it just turns your static data. Like, promise Scott resolve 42, it turns it into a promise that immediately resolves to 42.

Wes Bos

And that's great if you're trying to, like, keep the chaining or you're you're you're passing something to a function that expects a promise and not just a straight up value.

Wes Bos

So I think I think await kind of did away with the need for most of this stuff because if you have an async function that returns 42, that's the same thing. But if you need to turn a function that returns a value into a function that returns a promise of a value, that's where you use reject and resolve methods, the static ones on capital p promise.

Wes Bos

That's where do we at here? 23 minutes? Yeah. That's enough for a part 2. Hopefully, you learn a thing or 2. On pnpm Monday, we have the 3rd installment of the series coming out Wes we're gonna talk a little bit more about queuing and concurrency and running Yeah. Promises in series and whole bunch of libraries that are helpful for working with this type of stuff.

Wes Bos

Tech.

Wes Bos

Alright. Talk to you later. Peace.

Share