 I think we've got it. Morning. There's Stephen. Hey there. Good to see you. Let's say hello to Stephen. Claire, hey there, Stephen. Morning from Michigan. Michigan. Hello to Michigan. Great to see you. Wait a second. Now, what all the Michigan folks do, hang on. Maybe you've seen them do this. They do this. Oh, yes. See, all the Michigan folks do this. Oh, yeah. And that's where they live, because Michigan, of course, looks like it. Yeah, so you got that. I'm way up here. Oh, wow. Excellent. So Stephen, you're going to be talking to us about some C-sharp 8 feature. Yes, the asynchronous streams or asynchronous enumerables. Very cool. Can you share your screen for us? Sure thing. And no pressure. But the language team saw this submission, and they said, that one. I want to see that one. That one's really good. Excellent. OK, can you see my screen? We can. There we go. Now we can see your screen. Yep. OK, cool. Terrific. Take it away. This stage is yours. All right, thank you very much. This is a talk on asynchronous streams, as they're commonly called. I tend to think of them more as asynchronous enumerables because they don't have a whole lot to do with streams. They're not like an asynchronous version of the stream class. They're more like asynchronous enumerables in my mind. But asynchronous streams is the common name. Now, this is actually something that is available in C-sharp as of version 8. The same kind of concept is also available in TypeScript, in JavaScript, and also Python. So a lot of C-sharp devs also work in TypeScript or JavaScript. A lot of the same concepts are going to transfer over. And I'll mention that as I go along today. Yes. Yep, thank you. Sorry about that. So I'm Stephen Clary. I tend to refer to myself as the other Stephen, different from Stephen Tobbe. Stephen Tobbe works for Microsoft. He actually designed a lot of the stuff, and works really hard on a lot of the stuff that we're talking about today. I just write about it. I have a blog at StephenClaire.com. I'm on Twitter. And I wrote a book that just recently came out with a second edition that also includes information on asynchronous streams. So before diving in, I want to talk just a little bit why have asynchronous streams in the first place. And I'm going to approach this from two different perspectives. So one perspective is, oh, I don't know what that was. Some reason it went through all my slides. Let's try that again. All right. So one perspective is we have an enumerable. And we've got this synchronous series of values. So we can get multiple values. You can get one, and then get another one, and then get another one. And enumerables work great with the language. They're consumed by four each. They can be produced with a yield return. But there's these other things that are also part of the language. We've got tasks and asynchronous await. And we want to use asynchronous code within our enumerables. We want to do asynchronous work during our enumeration. This is not something that's easy to do in the enumerable world, but asynchronous streams enable this. And the other perspective for why we have asynchronous streams is if we have asynchronous code, but if it returns a task, tasks can only be completed one time. It can only produce a result once. What we really want to do is generate multiple results asynchronously. So we're looking at this merger of tasks and enumerables. And that's really what asynchronous streams are. Boy, I'm not sure what's going on there. I'm going to try clicking instead of the button press here. So we have this. When we're looking at what kind of return types to put on our methods, we have different options available based on whether we want to return multiple values or single values and whether these are computed synchronously or asynchronously. And even in the asynchronous sense, whether we want a pull-based system or we want a push-based system. So asynchronous streams fit in here when we want to return multiple values in an asynchronous way. That's also pull-based, which makes it easier to consume a more natural fit for the language. So let's look ahead at asynchronous enumeration. And let's jump right into a demo. So from the studio, here we go, demos. I'm just going to really briefly cover enumerator blocks because, in my experience, most developers are actually not familiar with this language feature. So here we've got a for each going on over here. And I'm just taking each item in this yield return method. Now, this yield return method is defined in what's called an enumerator block. You can identify it by the presence of a yield return in it. And the way that an enumerator block works is, as the for each pulls each item out, it'll call into our block. And at each yield return, this method will actually pause and return that method. So let's see that in action here. I'm just going to step through this. So the first thing that happens is we call this yield return method. And then we start pulling the first item out. So it calls into our yield return method. And we hit this break point. We're returning the first item. I'm hitting Go. And then it goes into our loop of body here, where we have an item of 1. If I hit a 5 again, it actually resumes execution of our enumerator block, our yield return here. And now we continue executing and return the second value, which is also then returned to this block. So actually, this yield return is a way to return multiple items. We step in again, yield return the third thing, and we get that again. Now this is kind of similar, almost the same, as this code here. This is what's more what's happening under the hood. So it's calling yield return. It calls a get enumerator. And what this returns is a type that can walk through all the values being returned. And then as we call enumerator.moveNext, that calls into the yield return. Now moveNext isn't really that important. It's part of the compiler generated code. But every once in a while, you'll see it inside of a stack trace. And this is where that's coming from. And once again, it calls in moveNext yields the next item. It resumes that method. So we have the ability to pause a method and return a value. And then later on, call into that method again with the moveNext and return the next value, which is a pretty interesting capability. And we end up with got123, got123 from both of those loops, and we're done. So let's take a look at what asynchronous enumerables look like. We have something very similar here, except we're using an await for each to pull everything out of this async enumerable. Now the difference with async enumerable is that we can define it using the async keyword. And now we can use await and yield return in the same method. Now the thing to keep in mind here is that this method can be paused one of two different ways. It can either be paused during the await. This will return an incomplete task from the moveNext method. And it can also be paused through yield return, where it actually produces a value. And then it waits to be called into again. So if we expand this out, this is really the same thing following a very similar pattern to the enumerator pattern, except it's calling getAsyncEnumerator. And the moveNext method is asynchronous. And then we have an await here to get that enumerator block completed to the next yield return. So the key takeaway here is that we end up with deferred execution that is like a regular yield return can pause your method. And we can also have this asynchronous pausing. So an asynchronous enumerator can be paused one of two ways. It can be either paused because it's asynchronous or it can be paused because it produced a value and is waiting to be called into again. I want to go into a little bit more detail before we dive into the more general use case. And one of the things that I want to talk about here is a couple of the new types. The actual shape of the asynchronous enumerator type is it is implementing iAsyncDisposable instead of disposable. And that's necessary to allow asynchronous cleanup. And that's all I'm going to say about asynchronous disposal today except to note that it is actually part of the language. And the other type here is that it's returning a value task of bool from moveNextAsync. Now, you may have thought, well, task of bool would be the obvious choice. Value task is a fairly new type. It's not new with .NET Core 3.0. It's been out for a little while. But most developers are not familiar with how to use value tasks. So I'm going to spend just a minute here and explain the pitfalls of value task. Value task is great. It's a more efficient version of task. But it comes with a couple of important usage restrictions. One of those is you can only consume it one time. Normally, you would await it and just keep on going. If you need to do anything more complicated, you should probably just be calling .asTask, which converts the value task into a task. So you can only consume it one time because the value task can actually be reused under the hood. And the other usage restriction that we have is that you can't block on it. It does have a result property. And it does also have the getAwaiter.getResult API. However, if the value task is not already completed, then that is not actually valid to do, that you get undefined behavior. Now, you don't normally need to know all this unless you're consuming value tasks directly. All the compiler-generated code, the await for each stuff, that will always do the right thing. And I do have a blog post about value tasks that is coming out hopefully later this week. One more note on the details before we continue on is that ConfigureAwaitFalse is actually supported by Async Streams. You can just stick on a .ConfigureAwaitFalse in your consumption there. And every await that's generated by the compiler gets that ConfigureAwaitFalse. So let's talk a little bit about our more realistic demo. This is similar to what Bill Wagner showed on day one. He was in the What's New in C-Sharp 8, part two, where he covered asynchronous streams. And he talked a little bit about a paging API. And this is one of the common use cases for asynchronous streams. So I have a server. It's already running here. And this is a very simple server that just returns a series of numbers. And it's got standard paging arguments on it. You can set the page size. You can say how far down the sequence you want to be. And it takes about three seconds to read these values from a database or whatever. So I have an API running on this computer locally. And I'm going to call into that with some code that's a more realistic demo of what asynchronous streams can be used as. So our main method here, I've got this paging API that I'm saying, give me an asynchronous enumerable of integers. Just give me a sequence of integers asynchronously. And I can consume that using await for each. And I've got some datetime stuff just showing when things come in to show the timing. Now the paging API is where we have our asynchronous enumerable. I'm just going to walk through this real quickly. The whole goal of this method is that this method wants to have all the paging logic in it. What we don't want is to produce like a sequence of blocks of items. So we want to handle all the paging in this function, keep that localized here. We don't want any of our other methods to have to know how things are paged. So I'm using a page size of five, start at the first item. And then I'm calling into this pretty straightforward, deserializing it into an array of integers. And then I immediately yield all of them. And then there's some housekeeping things like if we're done, then stop, and otherwise go to the next page. Now if I run this, we'll see some, take a note of the timing here. So we're starting and then we get five items all in a row and then we're paused and then we get five more items all in a row and then we're paused and then we get the last three items and we're done. So what's actually happening here is that as this await for each is going through our asynchronous enumerable, the first time it makes that call and that's actually asynchronous, we're waiting there. And then we get back a batch of results and we yield return all of those results all at once. Those are all actually like synchronous. So each one is yielding back to this await for each which then says, okay, give me the next item and it's already synchronously available. This is one of the main reasons why value task is used in asynchronous enumerables is because many, a lot of the time, only some of the calls are going to be asynchronous and oftentimes it's not most of them. Most of them are actually synchronous. So this is how a paging API can be used with asynchronous enumerables. Before jumping on, I do wanna make a quick note here that there's an anti-use case as well. Asynchronous enumerables are not for absolutely everything. When you have more of a notification API, for example, SignalR, we have a system usually where the client will connect to the hub, then it will receive multiple messages and then it will disconnect from the hub. Or another thing that's become kind of more common in the last few years are these HTTP streams that send out multiple responses, multiple independent responses. So in this kind of scenario, you'd connect to a socket, you'd get multiple messages on that and then disconnect it. Pretty much anything that follows this subscribe, one or more updates on subscribe kind of system is usually a better fit for observables because these APIs are naturally push-based. They're not naturally pull-based. Now you can translate them to pull-based, but that would require some kind of like a producer-consumer queue. If you do go down that route, I recommend looking at channels. Channels are a producer-consumer system from Microsoft that's very modern and performant and async-friendly. So that's the one that I would recommend if you do need a producer-consumer queue like this. So I'm gonna jump in now and talk about some link over async streams. We have linked to objects. That's been around for a long time now. We also have linked to events, which is or observables or system.reactive. Those are all the same thing. So we have linked to events, although that hasn't been as widely adopted. And we now also have linked to streams. Now this whole section is just C-sharp only to my knowledge. There's nothing in the JavaScript ecosystem yet that quite fills this need. Python does have support in their ITER tools, but for now we're looking at just C-sharp in this section. There's a NuGet package that you wanna install if you want to do link over async streams and that is system.link.async. Pretty straightforward name. Just a note here that this is in spite of the name, this is not actually a Microsoft project. It's a community project supported by the community. And it gives us all the standard operators. So we can now do dot where over an asynchronous stream or even join if you want to. One last note about system link async is that it is currently in preview. So the rest of this section is subject to change. And with that, let's look at a demo here of basic link. Oh, and if anyone does have any questions, feel free to ask them at any time and I will do my best to answer them. So we have a basic link example here. I've got a slow range. Now this method here I'm using in a few examples. So I'm just gonna walk through it real quickly. This counts from one to 10 or zero to nine. It counts up 10 items and it gets a little bit slower each time it counts. So this is a range that starts off pretty fast and then it gets slower and slower until the end. It's almost a second, I think, between them at the end. So here's an example using link. I'm calling the slow range method. I get back in async innumerable. And then what I wanna do is just take the even ones, just take the even numbers as the results. So I'm applying this where method saying, hey, just take the even ones. And then that produces me another query, just like regular link does with asynchronous link. Of course, we get back an asynchronous innumerable instead of an innumerable. And then I can just consume that query just like any other async innumerable using await for each. Now this code here, we can see it start off really quickly, zero, two, four, and then it slows down a little bit. Six, eight, eventually it gets done. All right, so that's your really basic link. Now, if it was just basic link, I probably wouldn't even talk about it because that's pretty basic. The really advanced thing that we can do with asynchronous link is that we can pass in asynchronous lambdas. We have a link to streams has overloads for asynchronous lambdas. Now, right now, the naming is very subject to change because this is in preview, but right now, any method that takes an asynchronous lambda has an await suffix. And I think that they did this because the idea is that these methods await the delegates that you pass to them. So the method name is quite odd for most coding conventions. It's called where await is the version of where that takes an asynchronous lambda. And these also return an isync innumerable so they can chain with other link methods very naturally. So the methods that end in await are the ones that take asynchronous lambdas. There's another way to do asynchronous in link and that is with what I call terminal operators. So these are operators that return of value. They don't return an innumerable. So things like count, count up all the members in this innumerable. These terminal operators end in async because they return awaitable. So these are things that you should await directly, not await for each, but await directly. So count async, for example. Now you can also even have overloads of these terminal operators that take asynchronous lambdas. So you can have methods that do both. And in that case, they have both suffixes and here's where it starts to get really awkward with count await async. Once again, naming here is very subject to change. But the idea, the core idea is that async link has all of these overloads available for us to use. I'm gonna take a quick demo here and take a look at some of those. In fact, I'm gonna take a look at the ones I just talked about. So in our async link demo, we've got, now we're really passing in asynchronous lambdas. I'm using that same slow range as before. Taking that slow range and I'm calling where await, because now instead of asynchronous lambda for where, I have an asynchronous lambda that I wanna pass in. And in this case that the asynchronous is just fake. But the idea here is that we can pass in an asynchronous lambda by using the where await operator. And this returns a query, which is just an isync innumerable, just like the one that we had before. And then we can await for each over that query and display each item. Go ahead and run this, although it's essentially the same thing as before, except each one is actually 0.1 second later. I'm sure that you noticed this. Yes, I noticed the 0.1 second for sure. Just kidding. So let's continue on down here with the next example. We're looking here at the terminal methods like count. Count terminal methods in asynchronous innumerables always end in async, because their underlying enumerator is asynchronous. So you can't have a count method, that doesn't make sense. If I were to type in dot count, it doesn't exist. Dot count doesn't exist. And it can't exist because it can't operate synchronously because it doesn't know or it can't block and wait for all of those items to come in. So the asynchronous enumerable, just the fact that it is asynchronous, forces all the terminal operators to be asynchronous as well and end in async. So in this case, I've got a result here and I count up all of the even items until I like even items. So I count up all the even items in the asynchronous enumerable and I await this count async to get an integer result. We are out of the enumerable world at this point. And then the final example here is terminal link methods that also take asynchronous lambdas. So this is something else that we can do. And once again, it's got the await suffix because it takes an async method and then it's got the async suffix because this is a terminal method. It ends in a value task of result. Note, oh, you can't even see the pop up, can you? It's actually returning a value task of int, not a task of int. Once again, all the enumerable stuff is more designed around value task than it is around, than it is around task for performance reasons. So that's asynchronous link and all the possible overloads that you can think of are available in that library, very powerful. It also gives you an ability to do what I call supercharging regular link. So this is a kind of a common scenario I've seen it asked on Stack Overflow many times. Hey, I've got this link expression. I've got this enumerable and I want to use an asynchronous lambda and a very common example of this is for the where operator. I want to filter things out based on some kind of API lookup or some kind of database lookup. That should be asynchronous. And I wanna use asynchronous lambdas for that but you can't do that with regular synchronous enumerables. Well, you can do that with asynchronous enumerables and what you can do is call two async enumerable to lift that regular enumerable to an async enumerable. And then you have all the power of asynchronous link available to you at that point. So let's take a look at that demo, run this one. So here I have this enumerable. Pretend that this enumerable ranges it here. I have some kind of enumerable from somewhere. I want to asynchronously filter this enumerable. Well, you can't do it directly but what we can do is call dot two async enumerable at this point, it's an I async enumerable and now I can call dot where await on it and pass in an asynchronous lambda just like I want to. Now I can filter it asynchronously. Now what we end up coming out of here is an asynchronous enumerable. So it does have to be consumed with an await for each not a regular for each. So just run this code. So here's a regular synchronous enumerable that has been supercharged, converted into an async enumerable and then applied and then it has an asynchronous where filter applied to it. Now one note here is that asynchronous enumerables are one item at a time. So as it's going through here, what it's doing is it'll take that first item from the source enumerable, do the await call here, whatever API call or whatever and then return the filter result, produce it to the await for each and write out the console dot write line and then the await for each comes around and says what's the next item? It gets the next item from the synchronous enumerable, it calls the await API one at a time, one at a time it's going through our asynchronous lambdas. That's the normal behavior for asynchronous enumerables, it's one item at a time. There is no built in concurrency, like it's not going to collect all the items and do a task dot when all across all and run the lambda simultaneously on all of them. So that is something to keep in mind when you are using asynchronous enumerables, there's actually no concurrency built in there aside from the fact that they're just asynchronous. So before we go, I wanna talk one last topic and that is canceling asynchronous streams. Canceling asynchronous streams is another thing that's really only supported by C sharp because they have a fantastic, or we have I should say a fantastic cancellation framework that other libraries and other languages are somewhat missing. So we have this cancellation framework built around the idea of a cancellation token. And the idea is if you have a method that needs to be canceled, you take that cancellation token as an argument to that method. With asynchronous enumerators, you also want to apply an enumerator cancellation attribute and the compiler will remind you if you do forget to add it. And the reason for that, I'll explain in just a minute. And then when you're ready to cancel or when you call an enumerator and you wanna pass the cancellation token into it, the easy way is you just pass the cancellation token straight in. But sometimes there are some scenarios where you can't do that. And so in that case, there's a with cancellation extension method and just a super fast summary of why it's because enumerators are cancelable and not enumerables. Yeah, so I'm not gonna get into all the details there, but that's the overlying reason of why there's a with cancellation overload. So let's jump in here and take a look at what that looks like. We've got simple cancellation here. I've got a cancelable enumerable up here. Now, this has the enumerator cancellation attribute. Once again, Visual Studio will remind you. There, it'll bring up the little squiggly and say, hey, you forgot your attribute unless you stick it on. Now, if I call into, this is the simple way of consuming a cancelable enumerable. I call cancelable slow range and I pass in the token immediately. I'm timing out after three seconds here. So this starts counting up to 10. It's not gonna make it the whole way times out after three seconds. Simple enough. This is the way cancellation works pretty much everywhere else in the framework as well. Asynchronous enumerables do have one extra little twist and that is here. If I've got a scenario where I've got some kind of method here that can take any async enumerable, right? And I want to apply a cancellation on it. For example, here I'm consuming a sequence but with a timeout. Now, this is a very generic method. I can call this on any async enumerable. That's what I wanna be able to do with it. So I say, all right, I've got this cancellation token source. I've got my timeout here. I can call, now what I cannot call is cancelable slow range because I don't know that's the enumerator that I want to cancel. I need to be able to cancel any enumerator. So this is why there's this with cancellation extension method. So this method, this more generic method can say, well, I want to apply my cancellation token to any enumerator that comes out of this enumerable. And then this actually also works. Now, this one is interesting. Does exactly the same thing times out after three seconds. So what's actually happening is this method here is calling cancelable slow range without passing the cancellation token. And it gets that enumerable back, passes it into this method. This method calls with cancellation. And what this does is the compiler says, okay, this token here that you're passing in here, you actually want to be stuck onto this enumerator cancellation argument here. So it's using this attribute to kind of attach that behind the scenes to enable this kind of a scenario. And this is a more advanced scenario of cancellation just to let you know that it is possible. Are there any questions? All right, then I guess I'm done. Thank you very much. Yeah, hey, sorry about that. There's like several buttons over here that we gotta press to get it all squared away. You want to turn your camera on for a little bit? Oh, sure. People can see you? Yeah, Stephen, we have one question and I'm gonna read it for you as I'm a DJI net. Is it acceptable to wrap a sync method in a task run method? And then it looks like we had maybe a correction. I mean async task run method to make an async version of the sync method. Okay, so that is a pretty common question. There's a great blog post that Stephen Toebrot about this called, should I write asynchronous wrappers for my synchronous method? And bottom line, the answer is generally no. The guideline that I'd like to give is you should use task dot run to call a method. You shouldn't use task dot run to implement a method. Okay, that's pretty good. So yeah, I always seen that as an anti-pattern, right? People are trying to make it work and because like, hey, async all the way down. So you're trying to call a method that it doesn't, it's not async around async so it looks prettier. Right, right. So there's different reasons why people try to do that but general case, it's not the best solution. You should either have the method, the method should either be naturally asynchronous or naturally synchronous. So if it's doing IO, it should be asynchronous. If it's doing CPU bound work, it should be synchronous. Even if that includes a lot of CPU bound work that would block like a UI thread, well, it's up to the UI thread to call that using task dot run, that's the idea. Oh, catch it, that's awesome. That's a great, great example there. All right, well, thank you so much for taking the time to talk to us this morning. Everybody, we're gonna wrap up here. We're gonna get Sean Wildermouth up and going. So give us a minute here and we'll get started with the next speaker. Thank you. Thank you.