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

Wes Bos

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

Topic 1 00:29

Promises for good understanding of promises

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.

Topic 2 02:45

Controlling promises

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 3 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 4 05:16

When to use abort signals

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.

Example of abort signals for search

Wes Bos

So all was initially rolled out when we got promises.

Topic 6 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 7 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

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

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

Topic 8 12:08

Error handling in promises

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 9 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.

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.

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.

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 10 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

Play / pause the audio
Minimize / expand the player
Mute / unmute the audio
Seek backward 30 seconds
Seek forward 30 seconds
Increase playback rate
Decrease playback rate
Show / hide this window