Best Practices - Async / Await | The Xamarin Show

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
>> Tune in to this week's Xamarin Show where my good friend Dean talks about best practices for Async/Await. You do not want to miss this, so tune in. [MUSIC]. Welcome back everyone to The Xamarin Show. I'm your host James Matzo [inaudible] , and today I have my favorite person in the entire world Dean with me, how is it going Dean? >> Good. How are you? >> I'm obviously absolutely fantastic. Now, people may not know who you are because this is your very first time on the show, and I'm really excited because we have a brand new micro series for best practices and Dean, you are an expert in everything, but tell everyone a little bit about yourself. >> Yeah. I'm Dean, I'm from the Microsoft Mobile Customer Advisory Team. We work with customers to help them figure out Xamarin best practices, how to improve their apps, how to get a good dev/loop going, and in general have a great experience with Xamarin. >> Yeah, that's awesome. It's really great because we have a great platform but so easy as a developer to get yourself into trouble. It's not your fault but sometimes you just write some bad code, I write bad code all the time Dean. >> Yeah. >> I was talking to Alex, big boss man Alex who will be on the show soon to talk about HttpClient and we're just like, "Man, I see a lot of the work that you're doing with the team, let's do a best practices series." We're going to have a whole series of this, this is the first video, Async/Await which I think honestly is one of those topics that I feel like I know a lot about but then I don't, and then I get people's code and I'm like, "I think you're doing it wrong." But maybe I'm doing it wrong and we're doing it wrong. What do you see out in the field with Async/Await? >> Yeah, that's what I'm here to clarify today. There are a lot of different ways to use Async/Await, and that's part of the problem. It's hard to understand what each different method, each different field is on the tasks, what all the different keywords are, and how they differ, because sometimes they're very similar and they were only created out of necessity and efficiency. >> Got it. >> Yeah. >> I remember when I started with Xamarin, I didn't even have Async/Await. Now, I did have tasks but I would always continue with and I was like, "Okay, I get it. I do this thing," then I'm like, "When you're done, do this." Async/Await it always felt magical because my code got so much cleaner and I was like "Just await, just await, just await," I didn't really think about what I was doing. >> Yeah, and it just works right? Until it doesn't. >> Well, let's get into it. >> Yeah. >> You told me you have a bunch of things that are not so good that people might be doing, right? >> Yeah. I have a simple application we'll have a link to the repo, and in this app we just have a bunch of different examples. On the left-hand side, we'll have the bad examples of what we see out in the field, or we see common mistakes or pitfalls, and on the right side is how to fix them. >> Okay, perfect. >> Yeah. >> I like that also by the way because it's like, "Here's bad code, here's good code." >> Yeah, exactly. This make it digestible, then also to see the contrast and see what actually happens when you run it, right? >> Perfect. >> Because a lot of times you look at Stack Overflow, we're looking at a sample and we contextualize in our head. But when we run it on the device, it's really hard to follow if you don't do it. >> Got it. >> Yes, let's just jump right in. For the first page, what we have here is your typical task, let me just explain the app a little bit. >> Sure. >> We have a "TaskService" that has this main method which is "GetStringWithTaskRunAsync". >> Okay. >> What this is, it's just a wrapper around a task. We'll see this is the main task, and it's just a wrapper so that I can print the different task statuses to the console or to the app itself. All it's doing is running a delay internally, and then we'll see the different task statuses come up on the screen. >> Got you. This is like your helper method that you're using. >> To illustrate. >> This is returning a task of strings, this might be very familiar if you're using HttpClient. >> Exactly. >> Get string asynchronously, you would call this get string with task run async and then you'd await on that, right? >> Exactly. >> In general, very similar but you're also passing in a bunch of arguments here like delays and cancel, are you going to go into those? >> Yeah. There's a bunch of default parameters but I did add those to show the different examples so we have flexibility. Some of them are just passing in an action so that we can run something after the delay and so on. >> All right, perfect, cool, lets go. >> We'll get into that as we go. The first one is just invoking a task, once we have that task, what the method returns is just a task string that we talked about. >> What that is saying is, "Hey, I'm going to go do some work, and when I'm done, I will return a string." >> Exactly. >> And the whole idea of a task and awaiting on it is long running process because I want to make that clear, because that's why you make a RESTful service call with HttpClient. Because that network request might take a few seconds and you don't want to block your UIThread, your awaiting is like a promise coming back. Like, "I'm going to go do some work, when I'm done then continue on," and that way it doesn't block your call, right? >> Exactly. >> That's your UIThread and that's why it's important here. >> Yeah. >> When I call this method that you have, it's going to return a string at some point. >> Correct. This is the bad way to start with, we'll see, we're invoking the method, but then we're "calling.Result" at the end of it. >> ".Result" >> Yeah. >> I wrote a whole blog post by the way on "Stop Calling.Result". Why is this bad? I don't understand. >> What ".Result" is, it will wait for the result of the task, and when that happens is when we call it, it waits, if the task hasn't completed, it just waits in line. We'll just actually stick here, then just wait till the result comes back and then we can continue execution. >> If that takes 10 seconds, you'll have locked your application completely for 10 seconds. >> Yeah. >> This is synchronous. It basically turns asynchronous method into a synchronus method. >> Exactly. You never want to do that. Let's take a look. If we click this, it will run for two seconds and then it suddenly pops up. You'll see while it's running, we can't open the menu or anything, that was really fast, I think it's one second now. >> Because I saw there was a delay there. >> Yeah. But you see the delay, you see the button animation sticks there. >> That's the best way to do it, right? When you click on something, it should continue, but that blocked to that button from even finishing its animation. >> Yeah, you don't want that. That was just one second. Like you said, if you have 10 seconds, users can delete your app just like that. >> Yeah. >> All right. Let's look at how to fix this. All right. This is the one-liner that we have, all we do is call await on the task and we get the result same as before. But the end behavior is, it just runs like that and the task completes asynchronously. We can still use the app, we can even run it multiple times, if you just click on this a bunch, it will complete and print out your completion. >> If you try and do it in a bad task invoke? >> You can't even. >> It won't even let you do it. >> It won't even let you do it. >> I think I might have just broke your app. >> It might have deadlocked, yeah. >> There we go, it's finally happening. But that is a bad user experience. >> Exactly, you never want that. >> You dropped the ".Result" and then you used "await". >> Yeah, simple stuff. All right. Moving on to the next one, let's collapse this down. Next one is multiple tasks. Once you've figured out that you can await tasks, the next thing I see is people await tasks separately. If you have like three REST services endpoints that you need to call, and you want to call them asynchronously, right? There's await first one, await second one, await the third one, and you know what happens? >> You're awaiting, and awaiting, and awaiting. >> Exactly. >> And you're not going to get to the end until everything is done. >> Until everything is done. >> Got it. >> Let's take a look at that, we have our "awaits" and a "for loop". >> Okay. >> Yeah, let's just run it. We'll see the first one go, second one, third one, and so on. >> That doesn't look too bad because you're awaiting, so you're not locking your UI but you're one, two, three, four. >> Exactly. >> That's the problem. >> Yeah. >> Got you. >> These tasks are just one second just to illustrate. But if you download the code, you can change the values and see what it feels like in real time. >> Got you. >> Let's take a look at the "Fix". The "Fix" is to actually keep the tasks in a collection. In this case, I'm putting them in an array and then we can call "Task.WhenAll" on the whole collection. >> What does "WhenAll" mean, what does that mean? >> "WhenAll" means that this task will only return once all the tasks are done. When a task is done, that means it either completed, or it was canceled, or it's faulted through an exception. >> Okay. Previously, we were taking these tasks and we were firing, awaiting, firing, awaiting, firing. Now, what you're saying is, you're going to fire all of them, and then when done, come back. >> Exactly. >> That's a thing of beauty, right? You can be uploading a bunch of files at once, and then the task here is going to figure out the threading, all the stuff for you, I can imagine. >> Exactly. >> Beautiful. >> Yeah. The task can complete separately. If some of them come back early, that's fine, it will just complete once the last task comes in. >> Okay, cool. What does that look like? >> Let's take a look. For the previous one, it took five seconds, we can see here. But if we run them with "Task.WhenAll", we'll see that they all fired off and then they all completed together. >> Because they all ran for a second, so they're all done. >> Exactly. >> That's even just literally a four second improvement because, guess what these mobile devices can handle. You have the bandwidth, stop choking your app. Cool, nice. >> Yeah. That's a really good improvement, we see that a lot. Next, let's look at returning tasks. >> Okay. >> This is tricky, let's look at the good one first because this is tricky. >> Okay. >> In our good example, we returned a string, we returned a task string. >> Yeah. >> What we actually returned at the end is this "Task.Run" task. >> Yes. >> This is the task, and then this is just all the harness code to print out the task statuses, and then at the end, we just return the task string. >> Okay, yeah. >> Okay. This is what we've been doing all along. I don't have to show the good sample, but for the bad one we're calling a different method which is here, "AwaitStringWithTaskRunAsync", and all that is is it's the previous method, but we just return await instead of returning the task directly. >> I see. This here is just like a helper method that might be doing some stuff, but we've added an "async" and "await" keyword here because we thought we had to. >> Exactly. >> Okay. >> The normal case for this is, if this is a library that we're using and it's an async library. Sometimes, developers think because it's async, we have to await. Then it'll just return await. But what you can actually do is just return the task like we did earlier, and that's actually faster because we don't have to await twice. Now, there's await in-between which does context switches and all this stuff under the hood. It's not much if you're just running a little bit, but it adds up. >> I see. The main problem that we're seeing here in general is there's already a task, and then you might have another method that is returning a task that you want to await on, because you get a bunch of gobbleygook in here too. >> Yeah. >> But there's no context, there's no IsBusy, there's no anything, but you're awaiting on it because you thought you had to. Don't await. >> Yeah. >> What's this context switching that you're talking about? You're saying this is generating other extra code? >> Yeah. Under the hood, this is generating an extra extra code that's unnecessary. We'll get into the context. But what it basically is is with Xamarin Apps, there's a main thread, which is the UI thread, and then there's a bunch of other threads, which depends on what device you're running on. >> Got you. >> Think of the different threads as a different context, and you always want to run off of the main thread. >> Got you. >> That's what awaiting does for us. >> I see. >> "where.result" actually ran on the main thread. >> Okay. Got you. Cool. >> Cool. For this one, this actually shows a big improvement once we run up to 1,000 tasks, but it takes awhile. On air, let's just run 100 and see what happens. I'm sorry, it's not that one. It was task return. This one. Okay. So we're running 100 tasks, and you'll see that even though we're doing "Task.WhenAll" just like before, they're not actually all firing off at once because ".NET" is smart enough where it knows like, "Hey, I can't run these many in parallel. Let me just chunk them off and run them when it's useful for me and to be performant." >> I like also that you're scrolling through and it's firing off more, but your UI is responsive. >> Exactly. >> It's like 13 seconds. >> 13 seconds. >> Okay. >> If we do "task.return await" on the task. >> This is the one where you're adding an extra "await". >> Adding extra "await". >> Also, we see that they're chunked in a little bit. They're not chunking here then because you're awaiting and then also chunking them. >> Yeah. Then we'll see nine seconds. Again, the difference is very minuscules for, this is 100 tasks, but I tried with 1,000 and it's almost twice the time. >> Got you. Awesome. What we saw, you ran bad task and then the good task, and the bad task actually was 10 seconds compared to six seconds. >> Exactly. >> Pretty good. If you did like you said, over 1,000, that would be dramatic. >> Yeah. >> Yeah. >> Yeah, and it adds up. The magic of tasks make it almost too easy for developers to abuse it, right? >> Yeah. >> It's like if you have a power tool, it's too powerful. You have to control yourself and understand it. >> The compiler is smart, but we just need to help it from time to time, right? And be like, hey, we can help you out a little bit when you actually need to await on it. >> Exactly. >> Yeah. Cool. >> Let's go to the next one, which is bad threading practices. >> Okay. >> Yeah. We talked about contexts before. This is where I'm going to illustrate. When we run a task, well, before that even, when we hit a button and run the command, it actually executes on the main thread. >> Yes. >> Once we await the task, what does it continue on afterwards? Is it still the main thread, is it a different thread? Developers don't think about this, right? It's like, "Well, it came from the main thread, maybe it's still," or sometimes they don't even think about the context because it's await, it works, it's just beautiful, it runs asynchronously. But then everything after that, you have to be mindful of where it runs. In this case, we have a thread sleep to simulate a long-running calculation or some sort of download image processing, whatever it is, and it's happening after an "await". Sometimes, people think they're safe. But what happens is, execution returns back to the main thread, and then this runs on the main thread. Let's take a look at that. >> I see. Okay. Got you. In this instance, when we call out to the service, it's going to start on the UI thread and actually await, do stuff in the background, come back onto the main thread, where threads are sleeping. But you may think it's on a background thread. >> Exactly. >> But it's not. Okay. Cool. >> Because the await was on a background thread. >> Got it. >> Let's take a look. >> Let's take a look. >> Yeah. >> It's started, it's created the task. >> Then it was sleeping. Okay. This is five seconds. We can actually see that the app is totally unresponsive. >> That thread, which is five seconds, has completely locked the app. Very similar to a result, also. >> Exactly. >> Got you. You are locking that thread. >> We didn't even use that "result". We used "await". Yeah. It's tricky in that way. >> Yeah. >> The way to fix this is to actually use the magic keyword "ConfigureAwait false". >> This is always tricky. This one's tricky. It always scares me a lot. Please explain. >> Yes. "ConfigureAwait", by default, a task will have "ConfigureAwait true". That just means return on whatever contexts was calling it, which was the main thread, right? When we say "ConfigureAwait false", we'll say don't return to the thread that we called on, just return to a different thread and we don't really have to specify what thread it is because that's all managed for us. But what this guarantees is that whatever happens afterwards, isn't going to be on the main thread. >> Okay. It's going to say, go do this thing, but we're awaiting, which means still don't execute anything after this until I've finished this thing. But I am telling you that it's okay to not return onto the UI thread, is that correct? >> That's totally correct. >> Okay. It saying, previously, we didn't use anything, so it started on the UI thread, went to a background thing, went to some task background thread thing, came back, now it's not going to come back. >> It's not going to come back. >> Okay. All right. Whatever you say. >> Let's see. All right. If you don't believe me, we'll take a look. All right. We'll start the good threading, then you can see that we can still open this and close it, and then it finally ended. >> Okay. Now, how is it able here, the "PrintStatus", to come back and actually update the UI because you just told me you're not on the UI thread? >> Yeah. Behind the scenes, we're using bindings. In the set property, it actually has an invoke main thread inside. >> I see. Okay. >> There's some safeguards by the good folks at Xamarin to help you update the UI. >> Perfect. I really want to clarify this. Here, when you say "PrintStatus" command ending, in that instance, that method is being executed on some background whatever thread, right? >> Yeah. >> If you didn't invoke on the main thread, it would blow up? >> It would blow up. >> It would actually crash your application? >> Exactly. >> Because you're trying to update the UI from the background. >> From the background thread. >> That's why you always really want to be sure where are you executing right now? >> Yes. >> Cool. Nice. >> That's the most important distinction for Xamarin. Sometimes .NET developers have a background with Async/Await, but there's no concept of a main thread. >> Got you. >> It's just a thread. >> Just a thread. >> Yeah. Actually, funnily enough, that's the next sample. The next one we're going to take a look at is updating the UI from the background thread. >> Okay. Got you. Cool. >> Yeah. Moving on to this. For this one, we're actually in the code behind because we want to update "StatusLabel.Text" directly. It's the same thing. We're awaiting with "ConfigureAwait false" because we know, "Hey, let's get off the UI thread." >> Yeah. >> But then afterwards, if we update "StatusLabel.Text" directly, bad things will happen. >> I'm ready to see bad things. Yeah. >> All right. I'll click this, and it actually worked. Sometimes it might work. Let's try again. There we go. >> There we go. >> We'll see an exception, "Only the original thread that created the view hierarchy can touch its views." >> Makes sense. >> I don't know why it did before, but this time it's saying, "Don't do that. Be sure to be on the main thread if you're updating anything in the UI directly." >> Got it. Your app has now crashed completely? >> It is completely crashed. Yeah. >> It's gone. Outta here. >> We've seen that in the wild. If this was in some sort of edge case and some sort of branch where it doesn't happen often, and then that one time that it happens is just catastrophic. >> Got it. >> Yeah. How to fix this? All we do is call "Device.BeginInvokeOnMainThread" and then put our code in there. >> Boom. >> Boom, simple as that. All that does is, it executes anything inside of here on the main thread, and we're good to go. >> Very nice. >> Let's take a look, and there it is. >> Nice. >> Updated from the UI thread. >> Now, the cool part about that method too, both part Xamarin.Forms and Xamarin.Essentials, is that, it does a check to see if you're on the UI thread first. >> Yeah. That's right. >> We handle that for you. >> Awesome. >> Very cool. Nice. What else? >> Cool. Okay. Next, we're going to look at "exception handling". This is something that is really, really easy to get wrong. We'll take a look at the bad exception handling first. With tasks, we talked about ".result", we talked about "await", but sometimes you want to "FireAndForget". Sometimes a task can just run. We don't care if it completes, if it doesn't complete. It's just, run. >> Yeah. Just go. >> Yeah. We just need to execute something. Let's say we need to try, "catch", right? We're done at developers. We use our catch statements. But in this case, what happens is because we've "FireAndForget" it, execution will continue till we're outside of the "catch block". >> Okay. >> If any exception happens here, who knows where it goes. >> Into the ether. >> Yeah. It depends on how your app is structured. Let's take a look. You'll see here, "command ending" will print first before the exception happens. Let's take a look. That's "FireAndForget". Actually, before that, I have to restart the app because setting the "StatusLabel.Text" actually broke our binding. >> Interesting. Yeah. There you go. >> Yeah. >> That makes sense. >> All right. >> Yeah, previously, you'd just have the binding setup. Cool. >> Yeah. Let's look at bad exception handling. We'll see the command ended, and then the task faulted. But we're not getting our print status exception occurred. >> That exception which was thrown by that "FireAndForget" is gone. >> It's gone. >> No one knows. >> No one knows. >> Your app could have some sorts of issues. It could be even crashing, but it's like you >> Exactly. >> Who knows? I'll fix that. >> Yeah. This is almost worse than an app crashing, right? Because with crashes, you know where it is. With this, it could be something layers down. >> Got you. >> Yeah. Moving on how to fix this. All you do is use "task.ContinueWith". What "ContinueWith" does is, once our task completes, regardless, if we awaited it or not, this continuation will happen and we can pass in an action. In this action, we have the task as a parameter, so we can inspect our task. We can also set these continuation options, where you can specify only on faulted, or only if canceled, or only uncompleted. >> Okay. That's really nice actually. You can do this "ContinueWith", decide how you want to handle a continuous. Successful? Don't care. >> Exactly. >> But if it's faulted, let me [inaudible] a message. >> Exactly. >> Very cool. >> One thing to note with "ContinueWith", is you can't have multiple "ContinueWith" on a single task. If you want to handle multiple different options, then just handle them all in the same "ContinueWith". >> Okay. Got it. >> Let's take a look here. If we do good exception handling, we'll see the command ended, then the test faulted, and we have our exception print. >> Nice. Very good. If you're logging that out into App Center or something, now you actually get that exception. >> Exactly. >> Very cool. >> Very useful. All right. Now, let's look at timing out. How do you timeout a task, right? Task just run. What if you want to set a time limit on it? How do you do that? The example that I've seen in the field is here, actually, "Task.WaitAll". Because "Task.WaitAll" has a second parameter which is a timeout. >> That seems convenient. >> Seems super convenient. Yeah. I was looking through the task API. This one has a timeout. It must work, right? >> Yes. >> Let's try it out. We'll see, the buttons jammed. >> Yeah. That's not good. >> Yeah. Same issue as before as ".Result", "Task.WaitAll" actually run synchronously. >> So "WaitAll", it's literally waiting. That would be all. >> Exactly. >> WaitAll. >> WaitAll. So anything with wait without an a, you always want a wait. You don't want to wait and you don't want result. >> You don't want result because those are easy to remember. >> Yeah. But I'm definitely going to contradict myself later in this example. Okay. Moving on, how do we fix that? This time timeout tricked us. We don't like that. Let's take a look at our good friend. Actually it's the new one. What I used to do was, I would create a separate task which was a delay and then I would do "Task.WhenAll" on that. >> I see. >> But in researching for this segment, actually find a better way. >> Okay. >> "cancellationTokenSource" is your friend. What you can do is, create a "CancellationTokenSource". What this is, is it's part of the TPL which is the Task Parallel Library, which is a mechanism to help you cancel tasks. Exactly what a timeout is, right? It takes a delay where you can pass in what your time span is. In this case, we'll time out after three seconds. How you pass in a "CancellationToken" and that's where the optional parameters came in before. You'll see I'm passing it in here. >> Got you. Pass in that token. >> Yeah. I'm just passing it into this "Task.Run". I'm right here, "CancellationToken". >> Got you. >> Then within the task itself, you can also throw it on "CancellationRequested". >> Okay. >> You have that fine-grained control, if you want certain things to throw or certain steps to actually check. >> Got you. >> Because "CancellationToken" will try to cancel the task. But if you just have a test that's just spinning, sometimes it might just keep going for more than you like. >> Got you. There's probably also the state in which you might need to pass this token down. Maybe you're going to into another third-party library. It takes the task cancellation token, and then you can pass it down. They can cancel it, and then it get bubble up. Very cool. >> Exactly. Super useful, works awesome for timeouts. Let's take a look. We'll just say good timeout, that's running. There's no UI lock. Then after three seconds, it cancels. >> Very cool. >> The underlying task still run, or it's not still running. It was supposed to run for 60 seconds, and then it just got canceled. >> Canceled. Very cool. That's really nice because you are just setting at a timeout. But I'm imagining that, with that you could also manually cancel. Maybe you have a cancel button for instance. >> Exactly, yeah. >> You're doing a long upload. No, I'm in this airplane right now, cancel. >> Yeah. One useful thing that we've seen is on navigation. Sometimes you want to reduce steps for the user, make it easier to use, but you also want it to be performing. Sometimes if they're doing something and they navigate somewhere else, that means they wanted to cancel. >> Got you. >> One navigation, you cancel. They don't even have to do anything. >> Perfect. >> The next one we'll look at "TaskCompletionSources". This is a more complicated mechanism of task, the task parallel library. But in a nutshell, "TaskCompletionSources" let us make synchronous code run asynchronously. >> Got you. >> It's a good way for us to create tasks with fine-grained control. >> That's great because often when you're dealing with iOS or Android specific code, there's not an asynchronous method. We might have like a callback or an event. You can Async. >> Or delegate. >> Yeah, or delegate or Async a file that API. >> Exactly. >> We do that with Xamarin.Essentials all the time. >> Yeah. >> You'll find these things scattered everywhere. >> Yeah. Once you're done with the sample, look in Xamarin.Essentials, and you'll understand the whole thing. >> Yeah. What's this thing doing here? Okay. Let's take a look at the good one first, actually. >> Okay. >> First, we'll look at what a bad "TaskCompletionSource" looks like. We're simplifying this API. But sometimes, we'll look in this method. There's a lot of code here. I won't go into all of it, but the gist of it is, internally, you have a "Task.Run" where you set result. This is almost the most important thing with the task completion source because if you never set the result, the task never completes. >> Got you. >> If we take a look at the bad example, what happens is, in here, I'm making it run 10 tasks with "WhenAll". But these tasks, before they're completed, they'll throw an exception. So what happens then, right? If their tests completed, then it's fine. But if it throws an exception, how are we handling that? >> It'll never get returned. >> Exactly. >> Because you never reset it through an exception. >> You never set an exception. >> Got you. >> Let's take a look at that. We'll see that these tasks actually are faulting, so the task faulted. But our exception here >> It was never printed. >> It's never printed. >> Got you. That's bad because now which is again into the >> Again, into the ether on multiple tasks. >> Got you. >> Then because we never set the task completed, then the task might run forever if it doesn't fault. This is faultings, it actually has a safeguard. >> Got you. >> But otherwise, who knows? >> Who knows? >> To fix that, what we do is in our new method here, here, in the right way, we'll see this is a different method, similar to before with all the structuring and harnesses. But in the "Task.Run" itself, instead of just setting the result, we actually have a check on a cancellation token. If it's canceled, we need to call "TrySetCanceled". Then in an exception case, we have to "TrySetException". >> Makes sense. Handle all the cases in which it can possibly go wrong. >> Exactly. >> Got you. >> In this way, we're totally safe. Any edge cases and exceptions, we'll handle them. Any cancellations, they're all covered. >> Very cool. That's nice. That's nice to see that, also prints out the faulted task, and stay in everything that you would need. >> Yeah. >> Very cool, and all the exceptions. >> Exactly. >> Yeah. >> A funny thing with the exceptions too. In our view model, we actually have a "try" "catch" around the "WhenAll". This one gets us the exception, but it's only the first exception that occurs out of all of the tasks. >> I see. Got it. >> Here, we'll see, if we print "ex.Message", that's only the first one. What we have to do is, we have to check on each task, what the exception is, if any. >> Got it. Got it. >> That's kind of a got you. If you're not looking for it, it's really easy to miss. >> Makes sense. Awesome, Dean. I love it because these are things that I can apply it to my app today. Now I know you have a lot more. We should probably take a break and come back for a part 2 advance. How do you feel about that? >> Sounds great. >> Awesome. Well, thanks everyone for tuning in. Dean, thanks for coming and showing this off. Appreciate it. >> Thanks for having me. >> Now, don't forget in the show notes below, we'll have all the links to the documentation, all the things you need. You can also go to aka.ms/xamarinbestpractices for everything in this series. Thank you so much for tuning in. I'm James Montemagno. This is The Xamarin Show. Have a good one. [MUSIC] >> Hey, James here. Just wanted to check in, and thank you for watching this video. Now, do all the things that you know you want to do such as "Like" "Subscribe", and ding that notification bell, become part of the notification squad. While you're here, check out all of these awesome videos that have already recorded, click on that thing. Click it, watch it, do it.
Info
Channel: Xamarin Developers
Views: 25,227
Rating: undefined out of 5
Keywords: xamarin, xamarin.forms, ios, android, visual studio, visual studio for mac, .net, C#, UWP, async, asynchrounous, async and await
Id: -LY4ATA8Bgw
Channel Id: undefined
Length: 32min 18sec (1938 seconds)
Published: Thu Dec 19 2019
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.