 Okay, so I've made too many slides, so I'm going to race through this a little bit. Hopefully it's not unpleasantly fast. If there's questions, please just interrupt me. I don't mind being interrupted. All right, let's go. I find that when I'm doing work, there's certain types of things which tend to generate more errors than others, and there's also certain types of errors which are worse than others. I find that a lot of people tend to set up their ears lint configs to catch errors which have low impact, as in they don't cause major problems, or those kinds of errors would be caught very quickly, so they're easy to diagnose and easy to resolve. The types of errors which I find that lintes have a bit of difficulty with, and typescripts are also not going to help you too much, or any of the static checking, are things like flow control and asynchronous behavior type of bugs, and what do we use for flow control and asynchronous behavior promises. So promises tend to generate some interesting problems in JavaScript. I've spent way too long on my first slide, but let's keep going. Major problems, confusing error flow control, errors that are difficult, errors which send you off looking in the wrong place. I hate that as the worst, and also errors which you don't even see. I also hate that, and these are actually probably my top four things which I'm looking for whenever I'm doing code reviews. Okay, so number one thing to do with promises. If you're creating a promise, always return the promise, even if nobody's consuming it. So there's a problem here in that first code sample. Is that readable at the back? Sort of, no. I don't know if I can even, whoop, that better? No, I don't think I can. Oh, okay. All right, so there's multiple problems with this first sample. First, it doesn't return the promise. So if anybody's using this load function, then there's no way for them to either catch errors or wait for it to finish. And the second fetch function in there, assuming it's asynchronous, it also, it hasn't been returned. So the entire function won't wait for it. That second line becomes, even if you return the first one, the second line becomes uncatchable, it's sort of uncontrolled. So that's no good. So async should always be controlled. Always return your promises. And even if nobody's listening, because one of the things that I find, it's very easy to assume that something which is generating promises is returning them. And it's hard to spot the fact that you're missing a return statement. If somebody sort of thought, oh, I don't know if I need to put the return statement in, just put the damn thing in. It makes it a lot, you'll thank yourself later. Oh, there's some encoding issue down there. So I don't know why there's two of them. Oh, no, this is the same slide. I'm just scrolling around it. Okay, here we go. I'm trying a new slide tool. And I discovered that it doesn't have a preview mode or like a presentation mode. So I had to export it as a PDF. And so this is what we're looking at now. Okay. The other thing that I see a lot of the time is people do silly things like this. They'll have something that's dealing with promises and they think that in order to, they need to return promise.resolve something. You almost never need promise.resolve unless you're attaching something directly to it, like promise.resolve.then. Yeah, whenever you've got a promise, you can just return, you can return anything and that the entire promise chain will always evaluate to a promise. You can't return anything other than a promise from a promise. You can't do it. So return values always turned into promises. Throne values also turned into promises. So if you're rejecting, there's really no point to ever reject. Just throw. It keeps your code sort of normal. It gives you like a keyword highlighting. It just makes it normal. Promise.reject. Looks fancy. Don't bother with it. Just throw. Those two pieces of code, well, other than the fact that the second one's missing an error message, they're equivalent. There's no difference between the two. Key thing here, yeah, don't sabotage your ability to debug. So here's another common thing I see is people rejecting promises with the string. This is really hopeless because you can't figure out where did this come from. You'll just get the string randomly in your code. And if you've done something like just said, oops, or there was an error, the only way to figure out what line that occurred on is to grep your code. And if you're having to grep your code, that's not so good. So a cool thing about the error object is that it automatically generates you a stack trace, which the debug tools can follow. So whenever you're dealing with, whenever you're throwing or rejecting, always throw or reject an error so that you get a stack trace just so you can figure out where the issue is. And the other thing I just thought of is when you're doing this kind of stuff, remember earlier I talked about like always return your promises. Now we have like async stack traces. So if you don't return, I think you lose the ability to get a proper async stack trace. So always return and always use errors. Big problem I see is catching too much. So spot the issues here. This might be a bit of a hard question. So we've got this thing. It calls an asynchronous function. It's wrapping like a callback interface. So, oh God, I don't know how to explain this. So it calls the get items. Get items takes a function. It does this promise chain. And then once it resolves, then it goes through the body at the top here. The problem here is that what happens if callback2 throws? So what actually happens here is that the code starts here, calls get items, does this async stuff, does the then, runs callback2. Callback2 is this thing which was passed into it. It hits here. Let's say this thing causes an error. So this thing throws. And then what ends up happening is that it ends up going down to this catch again. We've already, we're supposed to have left this function, but we're back in this function which then calls this thing again and goes back in and it'll create, basically you've gone out of the function. Then the error handler for the function, the child function, is catching an error in the parent. That's terrible. And I see this code all the time. It's really bad. So the way that we solve it, I think that was supposed to be an emoji. Yeah, it was. It was an emoji. It's very pretty. Why does this happen? Catch handles, anything that happens in this promise chain before the catch. So the on success in the then handler, it catches errors that happen in the on success. So what you need to do is use the second argument to then. This is probably the number one thing you should probably take away from this talk. You almost never need catch. You almost always want the second argument to then because otherwise you're catching errors in your on success. You don't want that. So second argument to then is the way to go. So this is the previous example, but corrected. There's no dot catch. It's just using the second argument. Yeah. And the catastrophic version of this was like the one I said before is when you when you end up, you're supposed to be returning control to the caller, but you catch the callers errors. And again, I see this code like literally this code all the time where dispatches. So for example, if the success handler here throws, then the catch happens. And then you end up with the chaos here is that you end up. It looks like two things happened when only one thing happened. And so that can be very difficult to figure out what's going on because you might think that they're actually supposed to be two. Maybe two events happened, but actually only one event happened. It was a success, which aired, which then caused the error. Anyway, so that's what that should look like. Second second argument to dot then same applies with catch. Okay. So the main thing that we're trying to do here is reduce the amount of things that you're catching. So this is bad. Again, I see this all the time. And I think the ES6 constant let things kind of mess this up a little bit because people don't want to have to put their variable outside the try. But you have to. So the issue here is if process uses throws, then we've thrown. We assume that the errors come from the fetch users call, but it might not have. It might be in process uses. And another tricky thing with promises is that you can end up having errors like the syntax errors can cause things to throw now. So, yeah, with one problem, number one is that we've caught an error that we shouldn't have. And two, we've assumed what that error was. So the correct form for that is to only put the try catch around the exact thing that you want to try catch. Yep. Both of the previous slides. So in some cases, the final function that you're not catching either in a try catch or a dot catch. That throws and that's intentional. Like you're allowing for that. You want it to throw. You don't want to catch all errors. Otherwise, especially you don't want to replace a useful error with a different error, which is unrelated. For example, in this case, error fetching users when the actual problem was that it had an error processing the users. So, yeah, another interesting thing here is that this is just this is bad code just overall. We're throwing away useful debugging information by with this throw new error. We already have an error. Why don't we just throw that error or just do nothing? Let it throw. The code above does actually adds no useful information. We should just let it throw if there's going to be an issue. Unless you can provide additional information, don't create new errors. Because the other thing is you lose your stack trace. Other issue is don't assume the type of the error. So this is a common problem. Okay, just one comment because maybe error, right? Unlike other programming language, we do have something like in the exception that can be read that's been read around it. So maybe that's the problem. Yeah. So what you can do if you are looking to, one thing that I've done in the past is you create a new error and you say you attach a property onto that error, which is like previous error. And then you can end up making like a linked list of all of the errors which generated the top level error. But throw away the old error and especially don't not log it. So here's another issue. Common problem. I see this all the time. I've probably written code like this. Try catch. What we're trying to do here is if there's no users, just return an empty array. So let's say that the fetch returns a 404 and assuming that's not the built-in fetch. Maybe it's Axios or something that actually throws when you get a 404. So in this case, the problem is if fetch fails for some other reason other than a 404, you're just throwing that information away. How the hell are you going to figure out what happened? You can show you can look in the debug tools, but this is sort of a general, as in the network panel. But if you've got lots of network activity or maybe you weren't doing network activity, maybe you're doing something else. This same sort of thing applies. So the issue is that we haven't checked that the error is the one that we are expecting. So here's a better form for this. We actually checked that the response status, you can't there, but I've checked that the response status is 404. In the explicit case of a 404, I'm going to return empty array. Otherwise, I re-throw the existing error. So whatever that is. So this is more code. I saw this code in some work code just today. You really don't ever do that. You're assuming that the type of error, it could be anything. You don't know what's going to muck up. It could be coming from anywhere. It could be coming from a third party library. It could be something that you've never seen. And this kind of thing might only happen in production. But yeah, just never assume the error and never just throw it away. So oh, yeah, there were cats. This is another thing I do this all the time. Make the sink. If the make the sink will throw, if the directory already exists. So we can safely assume that the directory already exists and continue. But the problem is what happens if it erred for some other reason. For example, out of file, out of file, iNodes, the disks for permission error. All these kinds of things. I remember seeing exactly this. No such file directly. I'm like, I just made it on the line above. What are you talking about? Especially because I've wrapped this try catch in a useful helper function. So I didn't even see that. I didn't even consider that this could be a problem. So stupid me. So solution is you should hear. I checked to see that the code is actually in. That's the wrong error code. But anyway, you check that the code is the exists. Otherwise you throw the error. So if there's something unexpected happens, you see it. So yeah, if you don't want to throw it like you don't want to exit the program, you probably do. But if you don't console log, just log it. Never throw an error away. Just log it. Always log it. Because there's been many times where I've been debugging something and the error has, the computer's been trying to tell me there's something wrong, but I'm just like, yeah, just silent. No, go away. And then when you finally figure out what line where it actually happens, you can see the error message says, oh, you know, this thing didn't exist. It tells you the exact problem. But because you didn't log it, you never saw it. And this can be really tricky to figure out what's actually going on. There we go. Don't let the computer tell you its problems. I'll try to rush through this one. Make async functions async. Do not release Zalgo. So Zalgo appears if you have a function which is sometimes asynchronous and sometimes synchronous. It's very bad. So more encoding. That one's, I know, that's supposed to be an ellipsis. This is Zalgo, yes. So the issue here, the fetch has, you know, it has some throws. Otherwise, it returns some asynchronous function. The issue here is, for example, on the line below, that dot catch will never work because the throw above it is synchronous. It never gets to the promise. And again, I've seen this code in real life stuff. So the solution to the, well, one solution to this is to, one solution to this is to put a try catch around it. So this one catches the synchronous errors and this one catches the asynchronous errors. But that sucks. Really what you need to do is just chuck that keyword in the async. And that means, so an async function will always be async. It always returns a promise. If it throws, it rejects the, it throws a, it returns a rejected promise. So that will work. AirBnB's ESLint rules will tell you that this is bad. It'll complain saying async function without using the keyword await. Ignore that. It's really stupid. And if you can't use async await, just due to whatever reason, wrap everything, this sucks, but it's an unfortunate thing that you probably should be doing. If you're, if you're returning an asynchronous function, if you're returning anything that returns a promise and you have synchronous error handling code, wrap it in a promise.resolve.then function thing. It's really ugly. But one thing, a great thing about this is that once you move to async await, you'll feel very good. Final thing here, cleaning up error paths. So spot the issue here. This is something I did the other day. I created a temporary directory, pulled some data in, removed the temporary. I didn't want to, I didn't want the server to crash. When I had an error, I just logged it. But the problem is that I didn't clean up my, my temporary directory, so I just end up, you know, filling my, my disk full of stuff. So the solution actually is this keyword, which is very rarely used, but I find it particularly useful for things like this, the finally keyword. So try catch finally, finally we'll, we'll execute before the return happens. So this return here, before this happens, this will run. And also before the catch is exited, this will run. So good for cleaning up. And there's also promise.prototop.finally. So you can chain it like this.finally. That's, that's a new feature. You can polyfill it though. And also there's some bugs here. What are the bugs? I don't know. So the problem here is that log error won't actually happen for import data. It'll only happen for the create temp because I've used the second argument to then, like I advised you to. The proper solution here is dot catch. Oh, and there was another bug. Ah, yes. So I legitimately made these bugs when I was writing the slide, and that's why I thought, oh, maybe that's a good example. The return await, that's a useful one. Without the return await, this catch won't catch this asynchronous, any errors that happen in this async function. So, but if you put the await in there, the end result is still the same thing, but the catch handler has an opportunity to catch any input data errors. So I think that's pretty useful. Summary. Always use errors when you're rejecting. Always return. I don't have that in there. Always return your promises. Use the second argument to dot then. Oh yeah, there it is on the third line. Put your tri-catches around the minimum amount of stuff. Never suppress your errors. Always log errors. Like that's just sort of like a mantra for me. If you have an error, never ignore it. And if you've got an error, check it before you suppress it. Use finally to clean up and ignore Airbnb. Its link config is terrible. Really. I live with it. My last two years have been using this config and it just makes your code worse. It really is bad. Thanks. Any questions? So I haven't written that much JavaScript in the last deal. But back then there was this thing whereby the advice was to always end your promise chain with a catch because otherwise in some situations you would just lose the error. So that was in the early days of promises. So the issue was that if you have a promise rejection and there's nothing, and it's like, for example, let's say it's in an event handler or in a set timeout where it's not connected to the main execution chain, the error can potentially just disappear. But since probably three years ago we've had unhandled rejection error handlers in both Noda and the browser. And so if you have an error, if you generate a rejection and then nothing handles that rejection, it bubbles up to the unhandled rejection handler in which case you can log it. So that was a real problem at the beginning. The promise errors would just disappear and that was horrible. And because that happened people decided they hate promises and they still hate promises. But, yeah, it's no longer a real problem. Sure, Brenda. I guess one more thing that I think you might want to add to your list of weird promise handling things is when you have multiple dot thens that are interspersed with dot catches. So it's like dot then, dot catch, dot then, dot catch. And I think that's a prime example of when you should use the second argument as the error handler. There is also the, I guess, in the early days, like Melvin's question, why they recommended to have a dot catch arrive at the end so that you would not end up in a scenario where it was never uncalled. Yep. Any other questions? Why? Well, do you think all these flexibilities provided by JavaScript's promises is like a bad design? I don't know. Maybe you can tell us in your next talk. The thing is that that's not really a question. I think it is a bad design that you're allowed to make so many mistakes just with such a simple thing that many other languages already got right. Interesting. You say that because the design for promises handling in JavaScript is modeled on other languages. So it's not just something that they made up and they went to great pains to get a good balance between power and flexibility. No, it's OK. It's pretty good. Once you start using async away, all this stuff becomes a lot nicer, I find. OK. I think you have one question about yesling. So what's your preference on the self-roofs? A lot of people are advising towards prettier these days. I haven't made the plunge to that yet. I still care too much about it. So prettier takes your code and formats it for you. It's kind of a little bit like go format. As in you write the code, it always comes out the same way sort of thing. And the idea is that to remove opinions about how code should be formatted, you put the code in. This is what it looks like. No arguments at the end of the story, which is sort of the idea behind yesling. But it's not, for example, prettier doesn't do things like the Airbnb config where it forces you to call your arguments particular names or it doesn't force you to, you know, it doesn't prevent you from using the i++ in a for loop. Another nonsense like that. Just who, what? Yeah. I think you should be using some sort of yesling, but I feel like just, I actually prefer standard. I'm not sure if you're at JSConf, but for us was there and he was talking about standard. It's also my preferred yesling config. It's fairly, it doesn't get in your way. It just sort of, just has a few loose sort of recommendations prevents you from like accessing variables before they're defined and catches a few simple, a few basic errors. Something that I think an yesling config should be permissive and shouldn't sort of, I feel that the Airbnb one is oppressive. It's really awful. Yeah. Yeah. Well, we just like, yeah, we have a, the work I'm at, we have probably this much config turning stuff off from yesling, the Airbnb config and then the files that I work on, I have another one which turns more stuff on. People haven't caught on to the fact that I'm doing this yet, but yeah. Yes and no, because it makes some things more difficult. Like I've actually got a line of code in my current project where I have an ESLint thing complaining, turning off the accessibility stuff just because I've got, can't remember, some simple thing. I just wanted to add an on click for like a utility purpose. It was to like suppress an event bubbling or something like that. But in order to get rid of all of the ESLint configs for that, it makes the line too long. So I have to have an extra line above it which disables the max line length ESLint config for the next line, which is an ESLint disable configs thing. So it's really silly. But yeah, it does have good recommendations, but often you need to ignore them, which is unfortunate. I use prettier and my colleagues hate me for it because I go around saving random files from writing them. But I found that I have 50% more time to go drink bubble tea. Excellent. Any other questions? Hang on, here's a question. Who uses an ESLint config? Or who uses some sort of code checker before they, in their project? Most people. Okay, cool. Who uses the Airbnb one? Okay. No, no, no, no, no, not the website. They've got an ESLint config, it's horrible. Okay.