C# Async Await Mistakes | Part 1

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hi everyone and welcome to my brand new series of where Austin goes to die this is a series of short videos in which I'm going to cover common I think await mistakes I have a strong feeling that after most of these videos then you'll go back to your code base and you'll change the thing or two because these aren't going to be things that you see every day these are mistakes that I don't see covered enough and I think it's important to cover if you're new to the channel then first of all welcome my name is amikai I do have a quick disclaimer that even though I work at Microsoft I'm not talking on behalf of Microsoft also if you're watching this and you're not subscribed yet then make sure to smash that subscribe button so you don't miss out on future videos and without further Ado let's jump into our example So today we're going to take a look at tasks that when all and specifically how we handle exceptions in this scenario this is done wrong in most applications so let's Dive Right In so let's imagine the following common scenario we have some client and it's doing some API call so if we look at what I did to set up this scenario and then we're taking the repository number and all we're doing is we're waiting the number of seconds corresponding to the number and we're returning the number so again here in the first line we're waiting one second and the value here is one and over here we're waiting two seconds and the value here is two now we can see that we're calling them one after the other so in total waiting three seconds but in many cases we don't need to wait for the result of one task to call the next task and we can do this asynchronously so let's imagine that that's the scenario in our application so instead of this we have get repo one task and the same thing we have here get repo number two and now we want to await both in the same time so we can see over here results and this equals yes test.1 all and we pass it both of the tests so we create here a new task that will be completed when both of these tasks are completed then by awaiting the task then we get the actual results which is an array of the results so what we expect to have here is array with two items where the first item or index zero is one and and index number one we have two so running this then as we expect this should take about two seconds and then we have over here one and then two now this is a very optimistic scenario of course our clients aren't always going to be so successful so let's imagine that instead of the behavior that we did instead it always throws an exception after that amount of time all right so let's say over here something that has the repo number yes it's following so again now we're calling it twice the first one will throw an exception after one second the second call will throw an exception after two seconds so if you're familiar with test.win all then I'm curious what you think is going to be the exception that's thrown so I'm running it you can pause the video and think about it while this is running okay so we got a result and we can see that the exception that we got is the first exception that was thrown it's not an aggregate exception of all the other exceptions or something like that it's only the first exception so first and foremost let's put this entire thing inside a try catch great now we know that this exception over here isn't enough because it's only the first exception that was thrown so instead what we might want to do is we might want to look at this test that we're creating and look at the exception property so the way this looks is we can say when all tasks and this will be test dot when all yes and then over here we'll actually await this task right let's just fix the typo yeah it's following so again we're creating here the task this won't throw an exception no matter how long we'll wait because it's thrown only when we await the task so we're waiting here at the desk then over here we have the when all tasks variable which has the exception property and if we look at this exception then it's an accurate exception and it should contain all the exceptions that were thrown okay so let's say for now some variable let's put here a breakpoint let's run it and let's look at what we have okay so we hit the breakpoint and then looking at the exception that was thrown then we can see that this is indeed only the first exception like we saw before but the exception property has all the exceptions that were thrown we can see it's an aggregate exception and over here we have both of the exceptions now you might think to yourself okay great so if I really want to throw all the exceptions that occurred then all I need to do is say the Rope on this thing and this will actually throw the exception the aggregate exception that contains all the exceptions we know that this isn't null because we only arrive over here when an exception was thrown well if that's what you're thinking or if that's what you're doing in your application then you are wrong because that's not the scenario and let's demonstrate when this isn't the scenario so let's look at our method again and instead of throwing an exception let's throw over here an operation canceled exception okay this is an exception that can be thrown if the cancellation token was canceled or if the method simply throws this exception it's not uncommon that such an exception is thrown so now let's debug it again and see what we have okay so we hit our breakpoints and now if we look at the exception then we see that safety like before it's the first exception that was thrown and it's in operation cancel exception but looking at the exception property then we can see that it's null now this Behavior might seem like okay so all we need to do is check if it's an operation cancel exception then to throw this otherwise to throw this but that's not the case as well so to demonstrate that let's create here another task and let's say that only task number two through rows and operation canceled exceptions and the other ones throw some other exception so this is easy to demonstrate let's say yes something similar to this this throws an operation canceled exception and this throws something that has the repo number yes it's following what we have now is we get a number if it's repo number two then we throw in operation cancel exception otherwise we simply throw an exception now when we're calling when all then we also need to pass it repo number three so these two should simply throw an exception and this throws an operation canceled exception what do you think is going to happen what do you think this is going to be and what do you think this is going to be okay so we hit the breakpoints and I don't know what you guessed but the exception that we have over here is again the first exception that was thrown that is the nothing operation cancel exception but one of them was an operation canceled exception so how do you think this affects the exception property and the answer is that it contains all the exceptions that are not in operation canceled exception which means that again is if all the exceptions that are thrown over here are not operation cancel exception then this will contain only the first exception and this will contain all the exceptions if all the exceptions that were thrown were operation cancel exception then again this will contain the first operation canceled exception and this will contain null if it's some combination where some of this is Operation cancel exception and some of it is just a different exception then this will contain the first non-operation cancel exception and this over here the exception property will contain only the non-operation cancel exceptions okay now the reason why this is problematic because it means we can't look not at this exception that is thrown and not at the exception property as the source of Truth to what actually happened the only place that we can look at that actually has the details are the original tasks where each one of them has the corresponding status that is correct and the exception if it contains the exception so for number two we can see over here that the status indeed is canceled okay another important note is that even though we're demonstrating it by throwing an operation Council of exception what you may have in your application is you might have some cancellation token that you have over here so let's say that's the token let's add this as a parameter then over here let's give it some default value and let's pass it over here to the delay method so what we have over here is we have some cancellation token we're passing it over here and let's imagine that it's canceled very early on so what happens now is that when we call this method then this will throw a task canceled exception which is derived from operation cancel exception and will run into the same scenario so even if in your application you're not explicitly throwing an operation cancel exception you may indeed fall into the category of what we're talking about today so now we know that only these tasks contain all the information that we need so now let's move on to the holistic approach of how to handle errors in this scenario so as you probably already guessed then what we want to have is an array of at least three tasks has this array over here and then we can use it over here so the way this looks is as following let's say tasks equal both new and yes let's create an array with the three tasks then we can pass it over here and now we have the tasks that we can check their status afterwards we can also take this and put it back over here because we aren't going to use the original test and also of course we can make it more concise by moving these three over here and then we can get rid of everything around and now this is a bit more concise now we don't care about which exception was thrown over here because all the details that we need sit over here inside the tasks array okay so what we have now are the three tasks over here and we're passing them to the when all then this when alcohol Waits until all three methods finish throwing their exceptions and then it takes the first one that's not a operation canceled exception unless all of them are operation cancel exception and that's the exception that's thrown we arrive at the catch block and then over here we have the tasks that now we can use to whatever air handling that we have if it's logging the exceptions if it's sending some metrics or if it's throwing an accurate exception that contains all the details now we aren't losing any information so that's it let me know how you do it let me know if you did something similar to this or if you're doing it in a wrong way in your applications today I'm curious to hear that's it and I'll see you in the next one
Info
Channel: Amichai Mantinband
Views: 31,317
Rating: undefined out of 5
Keywords:
Id: 07CFRGlISVU
Channel Id: undefined
Length: 10min 19sec (619 seconds)
Published: Mon Nov 28 2022
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.