#22 - Dart Asynchronous Workflows - All Futures, Streams, Async Generator Functions

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hey what is going on everyone i'm wicked welcome back to my channel in the first part of this chapter i showed you a comprehensive explanation on how isolates work inside art tackling some of the fundamental differences between synchronous and asynchronous events in the second part we also got our heads into how we can work with single or multiple synchronously generated values today it's finally time to check the third and last part covering up the asynchronous particularities of this chapter please pay as much attention as possible to this tutorial as you'll learn about two of the most important concepts of dart language features and streams so without further introduction let's get right into this tutorial in the previous part i started a tutorial by mentioning that asynchronous operation is more like a task that needs to be taken care of before proceeding to the next one right well can you guess what an asynchronous operation is not surprising at all an asynchronous operation is still a task but the major fundamental difference compared to a synchronous task is that we're not restricted to wait for the task to finish until we can get to the next one in an asynchronous environment we can process multiple tasks while the current one is still being taken care of now the best analogy i could make for this tutorial regarding these asynchronous operations is that we as humans can use some of our tools to work asynchronously in a really efficient manner say for example you want to watch a documentary on tv iron some of your shirts and wash some other clothes it would be literally three times more efficient if you do all of these tasks simultaneously instead of doing them one at a time therefore you can set the washing machine to wash your clothes while you iron your shirts in front of the tv watching the documentary in the previous tutorial we saw this really important table in an asynchronous workflow a single value that we know is going to be calculated and retrieved later on has a type of future on the other hand 0 or multiple values that we know they're going to be calculated and retrieved later on have a type of stream you might imagine that streams compared to iterables can emit all values asynchronously while other tasks are being taken care of in the background while iterables only emit the values synchronously one after another while no other task can be executed in the background thus highlighting the fundamental difference between synchronous and asynchronous workflows we're going to tackle features and streams in great detail in the following minutes so let's start with features first so when thinking of a future you must think of it as an asynchronous event we talked about those in the first part of this chapter back then we observed that a future compared to asynchronous stable defined type brings some uncertainty into the game is just like the future versus the present in the real world we're living in we don't know what the future will reserve us therefore the asynchronous event denoted by a future can be represented as a box this box can be under four states during its lifetime it can be unprocessed meaning that it's still waiting in the event microtask queue to be processed by the event loop uncompleted a state where it's been processed by the event loop but has not yet been retrieved the promise calculated value it can be completed with an answer in which the promised value arrived with an expected type and it can also be completed with an error in which the promise value didn't arrive and instead it drew an error a synchronous event on the other hand can be represented as a box with only three of these states unprocessed completed with a value or completed with an error the uncompleted state is not possible to be achieved on asynchronous event since synchronous events have to be completed before moving on to the next one in line i hope this really makes sense for you right now so in order to understand all of these particularities let's go ahead and power up vs code and see how we can play with those futures the best way to start this approach would be to take a look at some of the most popular constructors of a future and perhaps see how all of those execute and set their events on either the event or the microtask queue the first one in line is the default feature constructor this is probably everyone's choice when creating a future if we go ahead and check out the implementation we can notice it accepts a function as a parameter and this function can have a return type of either future or a normal type most of the features will accept both of these types as the computation parameter this is why we have two examples for each of these constructors also we can actually see that dart creates a new feature out of this and uses timer timer.run to run the complete method with the computation function we provided in this scenario dart has actually put the workers to work in order to complete that box we just talked about earlier another important feature constructor that we're going to use is future that delayed as you can see it is really similar to the default future constructor the only difference being that the future computation will be executed after the amount of time you provide as a duration parameter in our case i set it to 1 second so only after 1 second the computation will run and the future will try to complete note one important deduction from this a feature that delayed with a duration.0 is equal to the default future constructor as it won't wait for a specific period of time until it executed normal features like the ones we discussed until now are being set as events on the event queue inside the isolate and not on the microtask queue next up we have the future.value constructor this is a really special constructor as it won't take a function as a parameter but rather as its name implies a normal value or a future value future that value is used whenever you want to complete a future with a value immediately in this case it's like printing the value directly but in an asynchronous manner in this specific case the event is actually placed on the microtask queue since it has priority of being printed immediately this is obviously achieved whenever the value passed as a parameter is completed like in our first case however it can also take in a future as a parameter in this case our line of code is actually equivalent to writing this future with the closure returning 6 believe it or not dart will interpret this line of code as being this default future constructor since the future that value constructor can't complete with a value immediately future.sync is really really similar to future.value in a sense that instead of taking a value as a parameter it takes a closure a function as you can see from the implementation if the closure evaluates into a value that's not a future it simply calls the future that value constructor with the return value of that closure otherwise if it is a future it simply returns that feature so to sum up our future that sync constructor here is equal to future that value of 7. this is what dart is going to process at the end of the day our second future that scene constructor is simply equal to a normal future constructor since it cannot calculate it right away again the only difference is that the future that sync takes a closure while feature that value takes a value as a computation parameter future.microtestconstructor on the other hand is a little bit more different than the previous two if we browse its implementation we can see that it uses the schedule microtask function this function will actually place the event denoted by the calculations inside of it right on the microtask queue so compared to the previous that value and that sync constructors that resulted in a default future constructor if the computation parameter was a future thus on the event queue this being a microtask will result in a future but that feature will be placed on the microtask queue instead of the event queue the rest is pretty much the same at the end for spicing things up a little bit more i have added another set of default future constructors note that each of these futures will eventually complete in either a value or a future therefore print a number from 1 to 12. this number representing the order in which these lines of code were called in the first place we know that when running a program the isolate will synchronously read the code in the main file line after line so it will go like 1 2 3 4 up to 12 this way for learning purposes we want to know the order in which dart will print the result of this program what you might also have notice is this function that we call after we create the future then actually helps darth know what to do with the future or the box when it completes therefore inside this function you can have a closure you can use to do something with the response you receive later on we're actually telling dart to register a callback to what's going to happen when the value will be received in our case i set up the print closure so that it will actually print the received value basically if we had not placed this then function there all of the incoming values would have been lost because there's nothing that can listen to when they're coming it's like a bus arriving at a station if nobody is waiting for it in that station it just leaves and is gone forever as i said before features may not always result in proper values but can also end up in throwing errors as a result we also have a on error method we can chain next to then in which we can decide what to do if we got an error as a response to a future we also have a when complete function we can chain this is called whenever the future returns a value or an error no matter what it's more like the final statement in a try catch block having this in mind what do you think the output of this program will be if we run it right now if you watch the first and second part of this chapter you should be prepared to simulate it i'll give you 10 minutes to think about it you can pause the video and try it by yourself however note that as i said at the beginning of this chapter these may seem like simple stuff but you'll actually see from the example above how difficult these simple lines of code can get to interpret also if you find this tutorial really useful and you appreciate the way i teach these concepts please make sure to share the video hit that like and subscribe button and consider hitting the notification bell so that you'll know when i post a new video also make sure to follow me on twitter at let's get wicked this is where i'm at most of the time having that said let's get back to our simulation let's go ahead and run the program by typing in the famous dart run command and check the output now if you got the same output as the one showing up on the screen congratulations you absolutely master futures you're an absolute genius but if you are like me a couple of days ago you wouldn't be able to get this result not once in a million times now in order to understand where you are wrong i want you to pay attention to the real simulation process as it is very detailed and a little bit mathematical i would say so in order to start the simulation what i would recommend you to do as a primary step is to construct the event and microtask use as well as the output for the program i will also add another auxiliary queue that will represent the order in which dart reads the lines of code one by one notice we also have two synchronous calls one marking the start of main and one marking the end of it the lines of code are read by the isolate from right to left just like the event loop does okay so the first line of code is the synchronous print call this is processed directly by the event loop and as a result start is printed into the output zone the next line of code brings us to the first async event denoted by a future which is going to complete with a closure evaluating in one as a result the event loop knows that sometimes in the future the answer to this feature will arrive therefore we will place one which is the result on the event queue then we move to the next line of code denoted by this future which is going to complete in a closure evaluating into another future dart interprets this and knows that sometimes in the future a little bit later than the previous event the response to this future will arrive the result of this feature being another future will mark it as f of 2 for ease of understanding again note that the order in which the events will be processed later on is from right to left as the event loop is somewhere in the right part of these queues processing events one by one moving on to the next line of code we can observe we're investigating what's going to happen if we have this future.delayed constructor well in this case we can see that the future will be created after one second so we know that in both of these lines of code the response will come a little bit later on into the event queue the rest of the lines aren't delayed so most probably these two events will always sit at the end of the event queue just because the rest of the features will be processed much faster compared to this one second so let's go ahead and set them at the end of the event queue just like we did with the previous features but this time we need to remember that these will happen long long after the previous two ones so i'll space them out a little bit next up we have this line highlighted by the future that value constructor as i mentioned earlier since this value is a standalone value and not a future it means that the future that value can complete directly right away as a result the event won't be placed into the event queue but rather into the microtask queue so that it has higher priority as a result of this we'll place 5 onto the microtask queue the next feature that value constructor has a value of future sent as a parameter and according to what we discussed earlier we know that the event loop will treat this as being a normal future with a closure ending up in returning 6. as a result 6 will be added to the event queue but bear in mind it won't be added at the end of the queue since those 2 events are delayed by 1 second it will be assigned just before them the exact same thing happens to both feature that scene constructors the first one being a closure ending up in returning 7 can complete the future right away so it will be placed on the micro task queue the second one being a closure ending up in returning another future cannot complete our future right away so dart will process it as being a standalone future and will place the 8th event on the event queue again before the last two delayed events occur now moving on to the microtask part of our program this is where things get really interesting and unique bear in mind in this case both of these resulting events will be placed on the microtask queues no matter what as a result first and foremost 9 will be placed onto our micro task queue followed by f10 again pay attention even though this is a normal feature it's constructed with a future.microtask constructor that will schedule it on the microtest queue no matter what moving on to the last two normal features those will be placed again into the event queue but before the last two delayed ones so we'll have an 11 and an f12 right now the event loop scans the first line of code sees that's its synchronous and prints it directly as an output at this moment all lines of code were read the microtask and event queues were established and currently our console should only print start and end now the event loop will start processing the next batch of events but as we know from the first part of this chapter the events on the microtask queue have priority therefore it will process those first so it will process 5 print 5 process 7 print 7 process 9.9 then it will get to this feature that will have to process as surprising as it may sound the result of this feature which is 10 won't be placed onto the microtest queue but rather on the event queue because it's a normal future it will also be placed before these two delayed events as you can believe all the operations we've processed until now happen in milliseconds as a result we'll put it just right here now since the micro task queue is empty the event loop can move on to processing events from the event queue it will process 1 print 1 then receive this feature process it and place 2 again before the 2 delayed events at the end of the event queue then it will process 6 print 6 process 8.8 process 11 print 11 then it processes future of 12 just like with the previous ones 12 is set at the end of the event queue right before the two delayed events now the event loop will process 10 print 10 process 2 print 2 process 12 print 12 and process 3 print 3. finally at the end it will encounter the future of 4 process it and place 4 at the end of the event queue being the last event then the event loop will process this last event and print 4. both of the cues are now empty and the isolate terminates the program and this is the final output of this program it seemed easy at first but as you saw there were some pretty tricky events you had to consider now to clear things out even more i have another example for you can you guess what the output of this program will be i'll leave you again a couple of minutes to try and experiment so go ahead and pause the video and simulate it by yourself so this time we'll speed up things a little bit i'll create both of the cues plus the output right away and will process the events line by line the first line is asynchronous print statement so the event loop will process it right away and print one then the next line will schedule a microtask with a closure returning two as a result we'll place two onto the microtest queue then we have a future.delayed constructor which after a delay of 1 second will create a default feature with a closure returning tree as a result we'll place 3 into the event loop but again take in mind this is going to be delayed quite a while then we move over to a really interesting future so we have this feature chaining multiple thens what you need to know about this is that as we talked the first then will be executed whenever 4 will arrive in the event queue so for the moment we'll only place 4 into the event queue but before 3 which is delayed we shall keep in mind that this 4 is linked to the other then events so we'll move to the next line which is again going to place 9 on the micro task queue then we'll have again a nested future this time when 10 will arrive down the event loop we'll know we need to process these events too moving on to the next line as before the event loop will scan it and place 13 into the event loop but before the delayed event then we have another microtask which will place 14 into the microtask queue and finally the event loop receives a synchronous call which is going to print 15 directly into the console so for the moment only 1 and 15 will be printed into the console we have elements in both of the queues but since microtasks have priority we'll process those first so the event loop will process two print two process nine print nine process 14 print 14 and the micro test queue is emptied now onto the event queue it will process 4 print 4 but remember 4 was chained with multiple then callbacks after it what you need to know about these callbacks is that they will be executed immediately in this case they won't be enqueued into any of these queues as a result it will move to the first then print 5 then execute the entire callback inside the next then function in this callback will print 6 and place a new microtask 7 on the microtest queue we have one more then callback to execute which is going to print 8. now if we analyze the cues we can see that the microtask is not empty anymore as a consequence the event loop will have to take the next microtask events and process them until the queue becomes empty again so it will process 7 and print 7. now moving on to the next event we'll take 10 print 10 and since this is linked to this then callback we'll make sure to execute it before moving on to the next event as a result we'll process this first callback which is going to return a future then as a result of this we'll need to place f of 11 onto the event queue before the delayed event of course the trick here is that we're able to process the next callback only after the event loop processes the 11 event because that is the moment f of 11 will be completed with a value of 11. as a consequence the event loop will move on to processing the next event from the event loop which is 13 and printed then it will receive f of 11 then place 11 into the event queue before the delayed event when the event loop receives event 11 it prints it but remember it was linked to a then callback a couple of moments ago that then callback would get executed right now resulting in printing 12. the next and last event is being processed ending the workflow by printing tree and terminating the isolate process this is the output we should receive and as you can see it checks with the one resulting from running the dart program inside vs code i want you to observe that not everything related to asynchronous programming is easy whoever said that features are really easy to understand and practice is lying it will definitely take some time to get used to how everything goes where but at least for now you have a strong foundation you can rely on so right now let's head back to our first example we understood the concept of a future which is more like an empty box waiting to be filled with the result of that computation right but at the moment if you pay closer attention to our program apart from printing the result when it comes down one of the cues we're doing nothing more than that with the result we've been waiting for so long let's say we have a variable a we want the result of this feature to be assigned to this variable a suppose this calculation here is really important and is done asynchronously in the background the intuition will tell us well we can assign the value into the then callback here right so that whenever the callback is executed we'll just write a equals value this is 100 correct in this case after the future is completed with value one a will be assigned to one but obviously if you created this variable then that means you're going to use it somewhere else right let's say all we want to do it is print it a line below so that we can check if our callback works as it should what do you think this statement is going to print it should print one right well surprise if we go ahead and run the program dart throws us an exception telling us that local variable a has not been initialized yet why is that didn't we initialize it when we completed this future here yes the variable got initialized when the future completed that's 100 true but the variable we don't know from this equation is when is this future going to complete the truth is that nobody knows when a future will complete because this is the whole idea behind a future it will eventually complete but sometimes in the future nobody knows when not even the event loop a couple of minutes ago yeah we were able to estimate a little bit the order in which the futures were going to complete but what we didn't know is when they were going to complete as a result what we're actually doing wrong in this program is expecting the future to finish sometime before we call the print a statement right but what's actually happening in the background is start processing the future then registering a callback to execute whenever the future completes even though it doesn't know when the future will complete as a result it will move on to the next event this is the principle of asynchrony therefore it will process the next event which is synchronous but it notices that the value we're trying to print hasn't been yet initialized since the future hasn't been yet completed this is probably one of the most important async concepts to be understood then you might say well there must be a way to wait for the future to complete before we assign the a variable to the value right yes you might have heard of the await keyword this is absolutely its only purpose to wait for a future to complete so let's go ahead to see how we can achieve what we want with the await keyword well first of all we can get rid of this then callback for now we have this standalone feature right now after removing the then function nobody is listening to it anymore it may complete it may throw an error nobody cares but we want the value it completes with to be assigned to our variable well in this case all you have to do is to assign it directly just like this but you see this is a feature that's going to complete with an integer and this is a standalone normal integer therefore we need to wait for this feature to complete in order to assign it to our a variable as you can see dart isn't happy about this in order to use a weight we need to mark the function we're calling it as being asynchronous and we can do that by writing a sync right before the body now an async method will always return a feature since well similarly to how we're waiting for a future to finish before assigning its value to a variable others may also want to access the return value of our function so we'll need to let them know that the content inside of it will come in the future now if we go ahead and run the program dart will process this feature wait for it to finish assign it to the a variable and then move on to the next event in which it will print its value but then you might say doesn't this defeat the entire purpose of asynchronous workflows waiting for an event to finish before we can get to the next one yes this is correct using a wait will pause the entire thread until this feature is completed the idea here is that the computation function running in the background runs asynchronously we literally saw this a couple of minutes ago however if you want to know when exactly the future will complete you will have to use a weight and this will indeed make the workflow a little bit more synchronous this is why if you really depend on the result of a really high computational async process you may want to spawn another isolate and make that one process it while you're managing other events now what about the then callbacks this means they're useless in this scenario no they're definitely not they can be used as an alternative way of coding features we can actually use it to assign the a variable to the value with a then callback but still if you want to know exactly when that feature is going to be completed we'll have to wait for it so yeah overall i hope you understood how asynchronously returning a single value works with features but now it's time to get into something even more complicated actually if you entirely understood the futures part streams will look much more straightforward and easier to grasp so compared to features returning one single value in the future streams similarly to iterables can return zero one or multiple values the difference between streams and iterables though is that stream's values are not returned synchronously right away one by one but rather asynchronously in the future in order to understand this easier let me show you one of the simplest streams you can implement we're going to use the periodic name constructor to achieve this basically without looking into the documentation what this line of code actually does is emitting values from 0 to infinite with a delay of 1 second between them if we run the program right now we can see that nothing gets printed this is because just like in the case of a future the values coming down the stream need to be listened to if futures had the then callback well streams have a similar function called listen as a result we can call listen on our newly created stream so that whenever a new value will be emitted down the stream this listen function will be called and the closure inside will be executed therefore printing the value if we run the program again right now we can see the stream in action just notice how each value comes down the stream one after another this is the concept of a stream now in order to see that this is actually happening asynchronously in the background why don't we create another stream in the case of which we'll emit negative values from 0 to infinite every 2 seconds now you can clearly see if we run this program that for each two positive values we get a third negative value this is because the first stream is emitting values each second and the second one is doing it every two seconds while they're both running asynchronously in the background and observing what we previously discussed about features we know that features complete with a value in the future right as a result we can actually construct a stream from a bunch of futures using the from futures constructor note the asynchrony support again since if we set these two features as a parameter to the constructor they will be printed again in the right order since the future that value will complete first therefore it will be added to the stream earlier than the normal feature i want you to observe how everything starts to make sense right now generally speaking when we talk about a stream we should think of it really as a stream of water you can think of the values emitted down the stream as being some ships containing those values of course those ships are added to the stream by some authority they can't simply appear out of nowhere and they obviously need to have a destination doc where someone expects them aka listens to the values coming down the stream the authority adding them to the stream is the stream controller while the dock expecting every boat to arrive is a stream subscription know that by default only a single stream subscription can listen to a stream the stream needs to be a broadcast stream so that multiple subscriptions can listen to it having all these components in mind you need to know that you can use them to create a stream of your own for example the first object we need to create is the stream controller instance as i said this is the authority that's able to add ships down the stream by creating a stream controller we're actually linking it to a stream because a stream controller will always compact with a stream as you can see we can actually access it by typing in streamcontroller.stream this is a getter returning the created stream so currently we have initialized the stream and the stream controller but how is this controller going to add ships with data down the stream well for this we need to use the add method from inside the stream controller class we want to achieve the same behavior we had with our stream.periodic constructor as a result we'll create a timer object with the help of periodic constructor this timer will execute this callback for every second for an undetermined amount of time in our case so before the timer runs we can create an integer value and assign it to 0 then for every tick of this timer we can add the current value to the stream by using the streamcontroller.add method and then increment the value remember from long ago if we had plus plus value instead then the value would have been incremented before being added to the stream so we implemented the stream controller the stream and programmed how the controller adds new values down the line but now we need to set up the dock listening to the stream as a result we'll just type streamcontroller.stream.listen and for each of the received values we'll print them just as before now if we run the program we can observe we have the same functionality just as with the stream that periodic constructor but at least now you know the functional components of a stream note that this listen function returns a stream subscription just as we discussed so we can actually assign it to a variable but now dart shows us this warning mentioning that we haven't written a line of code that's going to close this subscription when they won't need to listen anymore to the stream this is a vital concept to be understood when you create a stream subscription object it won't stop listening to the stream until you'll manually cancel it ideally you'd want to cancel it after you're not interested in the values of the stream anymore or whenever the stream won't simply emit any more values so for example in our case if our value ends up being 5 then we'll cancel the timer as well as the stream as you can see from the code right now as a result the stream won't emit any new values anymore but the stream subscription object will still be listening to the stream which can lead to memory leaks so if the value becomes 5 then we'll also cancel the stream subscription now the warning is gone and everything works just as it should be what i want you to also understand is that by default only a single stream subscription can listen to our stream if we try to create another one listening to it and run the program dart will throw a bad state exception mentioning that this stream has been already listened to in order to have multiple stream subscription listening to the same stream the stream needs to be a broadcast stream as a result we must create a stream controller with the help of the broadcast constructor now if we go ahead and run the program we can see that both of these stream subscriptions listen to the same stream therefore printing each value twice so i hope you kind of understood the main components of a stream but now the same question arises just as in the case of futures do we know exactly the moment when a value will come down the stream maybe we'll need for example to retrieve the biggest value emitted by the stream how are we going to do that obviously we'll need to wait for the values of the stream in order to calculate which one is the largest as we don't really know when each of the values will arrive to the stream subscription we will comment the stream subscription implementation for the moment so we know that in order to wait for a single feature to complete we use the await keyword but remember a stream will return multiple values as a result we will need to use an await4 structure so for each value we're waiting for we can calculate the maximum value directly and when the stream will get closed then the await4 will also exit and dart will print the max value on the screen we can also achieve this again by using the for each method directly so that for each value we retrieve we calculate the new maximum but don't forget to await for it to loop through all stream values you see streams are really similar to features in many ways as instead of returning a single value in the future streams may return 0 1 or even multiple values in an asynchronous manner streams are a huge topic to discuss and it would take me ages to do so i'm trying to show you the major concepts so that you'll start a journey of understanding them with a solid foundation you need to know that we're not done talking about the methods on how you can create a stream of data yet i want you to remember from my previous tutorial how we were able to create an iterable by using asynchronous generator function well in the same manner we can create a stream of multiple values by using an asynchronous generator function as we're going to see up next so creating an asynchronous generator function is absolutely similar to creating a synchronous one just as we saw in the previous tutorial this time though the return type won't be an iterable but rather a stream and the keyword won't be syncstar but rather async star let's say for example we want this async generator function to generate values from 0 to 4 onto the stream to achieve this we'll have to use a standard for loop from 0 to 5 and then use the same yield keyword to add the values down the stream this is just like we were calling the stream controller that admitted before obviously since this generator returns a stream we can call the listen method on it and print each of the elements we will retrieve if we want them to be a little bit delayed we can actually await a future that delayed with a duration of one second inside the async generator it's that easy oh and also similarly to how a synchronous generator could yield another generator with the yield star keyword the same happens in the case of an asynchronous generator we could for example yield another stream within our mainstream just like this and everything will work just as expected i want you to understand and see by yourself how abstract and interesting streams can get they can be really useful in multiple scenarios and they represent the foundation of reactive programming a design paradigm that relies on asynchronously programming logic to handle real-time updates to otherwise static content we'll use streams all the time in future flutter tutorials if you want to learn more about the power of streams i would highly suggest you to go right ahead and check out the eric's dart package a package that will provide so much more functionality to the already impressive dart streams api and perhaps let you understand more concepts about what can be done with streams having this said i think it's finally time to end this dart sync and async chapter i hope you understood everything in great detail at least the foundation you need to perfect your skills in becoming a dart and flutter expert we have finally arrived at the end of this amazing dart from novice to expert tutorial series with everything we learn in this course i will create a small and interesting command lite application that will help you understand and practice every concept in detail however this tutorial video as well as other future updates on dart will only be available to those of you buying the full dart course i'll launch in the following week on udemy so stay tuned for that i want to thank everyone for watching this amazingly long series of learning dart from a novice to definitely an expert level and i hope you really enjoyed every single tutorial i've made thank you as always if you like this tutorial don't forget to smash that like button subscribe to my channel and share the video with all of your friends and colleagues in pursuit of top tier development until next time as always take care wicked is out bye bye
Info
Channel: Flutterly
Views: 1,947
Rating: undefined out of 5
Keywords: dart, dart tutorial, dart async, dart asynchronous workflow, dart streams, dart futures, dart isolates, dart async*, dart async generator, dart async generators, dart yield, dart yeild*, dart timer, dart timer.periodic, dart future delayed, dart future value, dart future microtask, dart future, dart streamsubsription, dart streamcontroller, dart stream listen, dart listen
Id: Ky8RxFF4kug
Channel Id: undefined
Length: 43min 18sec (2598 seconds)
Published: Mon Sep 20 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.