How To Easily Do Asynchronous Programming With Asyncio In Python

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
whether you're doing web scraping retrieving data from a database or interacting with an api knowing about asynchronous operations is crucial in this video i'm going to show you how async work how you can write code that runs in parallel so that your application becomes much more efficient before we start i have something for you it's a design guide that i wrote that explains the seven steps that i take when i design a new software application it's available for free at rmcode.com design guide contains really practical actionable stuff you can apply immediately to the code you're working on and improve the way you design your software so get it for free at ironcodes.com design guide i've also put this link in the description of the video now let's take a look at today's example the example that i'll be looking at today is an internet of things service there's a simple main file that creates an internet of things service objects and then registers a few devices smart devices like a hue light a speaker and a smart toilet i don't really want to know what that means but anyway so each of these devices gets registered at the service and then you can run a couple of programs like there's a wake up program which switches on the hue light switches on the speaker and then plays a song on the speaker and there's a sleep program that switches off the hue light and the speaker and then also flushes and cleans the toilets for you which is really useful and then we run these programs there's also a package here local package called iot which contains a couple of classes that we're using here in this main file let me actually make this a little bit smaller so you can see it better there's a devices file that contains the various devices right so there's a hue light device and a smart speaker device and a smart toilet device and each of these have a couple of methods so there's connect disconnect and send message send message gets a message type which is in this file and message type is basically an enum to switch on or off or change the color of light or play a song open and close etc etc and then there's a data class message that contains a device id for whom the message is intended the type of message that you like to send and the string containing some data and then finally we have a service which has a helper function to generate an id it uses a protocol to define what it expects when a device is registered in the service so the device should have a connect disconnect and send message method and the service itself is pretty straightforward you can register devices unregister them it basically keeps track of devices in a dictionary and generates ids when you register them and then we have a run program method that takes a list of messages and then sends them to the right device by using this send message method here that looks up the device in the dictionary and then sends the message so pretty straightforward let me run this program and then i can show you what is the output so you see it gets a couple of connection messages and then when it runs the program it shows what happens when the message is handled now of course these are not real smart devices that i'm connecting to it's mainly an example to show how asynchronous operations are going to work the traditional way of dealing with asynchronous and parallel code is by using threads whenever you start an application this actually launches a process that's the operating system that does that for you and a process is basically a piece of cpu and memory that you get allocate within a process you can have one or more threads the operating system schedules how much cpu time each process gets and for how long so what's the difference between threads and processors well processors really get a separate piece of memory assigned to them whereas threads can actually share memory threads are actually quite useful because often you have to wait for things for example when you open a file you have to wait until the data becomes available or you have to wait for the response of a network and with threads this means you can do other things while you wait even though threats are quite powerful there's also a couple of problems with them one problem is that because they share memory you easily get race condition bugs where threats try to read and write data at the same time leading to all kinds of unpredictable behavior and crashes another problem is that programs in general become a bit harder to understand if you have multiple threads because as a developer you're going to have to think about what it means when the different threads interact with each other so that also means that even though you can create a program that is multi-threaded it doesn't have to mean that in every program you just have to use threads just because you can because they complicate things and the third issue is they actually introduce some overhead because there is a part of the system that has to manage the threats stop them restart them etc and that also takes cpu time there's an alternative to threats and that's called asynchronous programming this relies on something we call a future or a promise or a delay or a deferred and these things describe a sort of proxy for an object that's at this moment yet unknown and is going to be resolved later in the future normally because the computation of that object has not yet been completed in javascript these things are called promises in python they're called futures they're not exactly the same there's a slight difference between them but they're used in more or less the same way but generally when you write asynchronous code yourself whether that's in javascript or in python you won't very often encounter these objects directly because there are syntax extensions that help you write code that uses them but you don't have to create these objects yourself futures and promises come from functional programming these terms already appeared in computer science academic papers in the 70s actually barbara liskov whom you might know from the liskov substitution principle of the solid principles has played a key role in defining futures and promises and how they should be used if you want to learn more about the solid design principles i did a video about that a while ago you can watch it right here so now let's change the code in the example to use asynchronous operations instead especially in python 3.10 asynchronous operations have become a lot easier to deal with in your python programs when you want to run an asynchronous program you can use async io to achieve that very easily let's first import async io and i'll show you what i mean so that's the async io package and now in order to make main asynchronous the only thing we need to do is write async in front of it and now main is an asynchronous function and now there's a problem here because we're not calling it asynchronous we can run main asynchronously very easily using the asyncio.run function there we go now we have our first asynchronous program obviously inside the asynchronous main function there is nothing that's actually relying on things being asynchronous so when i run this i'm getting exactly the same result as before but now let's turn our devices and our servers into an asynchronous device and service which makes sense right because if you connect to a device normally this happens over a network so you're gonna have to wait for the response for that connection to be successful so that's a typical asynchronous operation and what i'm going to do so in my devices classes i'm going to turn these connect disconnect and send message methods into asynchronous methods so we do that in exactly the same way as for the main function you just write async in front of it let's do that everywhere there we go now each of these devices is asynchronous and let's also add some actual asynchronicity asynchronicity in it by introducing a sleeve to simulate a connection delay so for that we're going to import async io because i need the sleep function and let's add here a async i o dot sleep for half a second and let's put this everywhere there we go now whenever we call this method it's going to take half a second for the response to come in and that's going to affect how we run the programs in our main function so we have our devices that are now asynchronous the message part we don't need to change because this is just a message representation and the service we can now also make asynchronous so instead of expecting a device that has standard python methods we can now create a protocol that expects asynchronous methods and then we can also make the surface itself asynchronous so that these methods are also going to be asynchronous this one doesn't need to be asynchronous running the program does need to be asynchronous and sending the message also needs to be a synchronous now i see we're getting a couple of errors here because we're now calling methods inside these send message and run program methods that itself are asynchronous like for example let's look at registering device so there's the connect method which is asynchronous so async what we've written here in front of each of these method names indicates that this method is run asynchronously we have another keyword that's called awaits which tells us that we should wait for that asynchronous method to finish before we continue with the rest of the code so that's what we can write here so in this asynchronous method we're waiting until the connect method is finished and then we're going to do these things stay see it works very differently from threads where we don't have this mechanism at all but i find this actually really intuitive way of working with asynchronous operations like database connections or connecting to devices like we're doing here same thing for unregistering device so i should also add a weight here so this waits until the disconnect method is completed and then it's going to run this line of code and for running a program i can do the same thing so i'll write here a weight in front of this send message call and in send message i have to do the same because this is also an asynchronous method so there we go now everything oh sorry this should be await obviously so there we go now everything here is done asynchronously which is nice and in the main function we can now also call the methods asynchronously so that means that we should put a weight in front of the register methods here like so and running the program is now also asynchronous so what happens here well we wait one by one until we've finished registering these devices we define the programs here and then we run these programs in sequence and we wait every time until they're finished and now when i run this let's save the file and run this you see that it actually didn't work and that has actually a very simple reason in that the sleep routine that we're calling here we should actually also await it otherwise this doesn't work obviously so it's actually the exact same reason we need to wait until the sleep function has finished in order to print this message so now let's try this one more time and now you see everything is taking half a second so we're connecting and we're running these two programs and awaiting each result in order until now we didn't do much more than adding a couple of sleep operations and waiting until they finished so that doesn't really sound like a big advantage but let's change the example now to use parallelism and that's going to take this code to the next level now that we've set up an asynchronous program like this we can start to profit from asynchronous operations because when you look at the code actually it's not very efficient we're waiting here for every device to connect but actually there's no reason for this to do that because these are independent devices the hue light and the speaker and the toilet so we could potentially just send out the connect messages all at once and simply do that at the same time and then just wait for everything to come back again we don't have to wait for the hue light to register in order to register the speaker and we don't have to wait for these two things in order to register our smart toilets which is independent from the hue light and the speaker so what you can do instead is use a function called gather that's also part of async io o and that allows you to run things in parallel so just to show again what's happening at the moment before i change it so i'm just going to disable running the programs but you'll see that it waits for every connection to finish in order to connect the next one right so that takes about two seconds now or one and a half seconds and let's go back up here and that's because we're calling these await statements one after the other now let's set this up slightly differently using async io dot gather so this is what that looks like so gather takes a number of arguments each is an async call tool function right registering a device and it returns a tuple containing the results of those function calls but the nice thing about gather is that it's actually calling this in parallel so you remember before we saw this connection happening one after the other so if you now look at when we run this code we get an error gather itself is again an asynchronous function so we should write a weight in front of it you remember before we saw that connecting to each of these devices happened one after the other now let's look what happens when we connect it in parallel so you see we get immediately three connecting messages it waits a bit and then we get the connected message back and this is of course now much faster because all these sleeps all these waiting for connections things are going to happen in parallel and that's just saves a lot of time so by combining both these sequential and parallel operations in your code by thinking about what you can do in parallel you can actually achieve a lot of speed up and a lot of performance improvements and here it saves a lot of time it basically saves two thirds of the time because we can wait for all these connections in parallel we don't have to wait for one in order to connect to the other and we can do something similar for running programs so in this case we have these programs that send messages right so we have switching on a speaker and playing a song etc flushing and cleaning the toilet and then we run them so let me just remove the comments sections here so then we can run these programs again but at the moment if you look at the surface you see that the programs are also running in sequence because they're in a for loop and every time we call a wait so we wait until each of the messages is sent until we send the other one and that results also in a lot of delays so you see we get every message one by one and it waits until it receives the next one but that's not necessary because well we can switch on the hue light and the speaker at the same time it doesn't matter right one thing you could do is that run program sends the messages in parallel instead of sequentially and you can do that by replacing the for loop with list comprehension and that looks like this so calling the gather function i need to import async io and then we're creating a list self dot send message for message in program like so and because gather expects arguments we need to unpack this list as follows and now we can delete these two lines and now run program runs it in parallel so if we go back to the main function let's run this again so you see we get the connection and it's running the programs much much faster but this might actually not always be what you want because there's some problems here in that there are dependencies in this program like here i'm switching on the speaker and then i'm playing the song i have to wait until the speaker is switched on until i can play the song and here similarly i'm flushing the toilet and then i'm cleaning the toilet i don't want to start cleaning the toilet while i'm flushing it that's going to be a huge mess probably so it's a bit more complicated than that in the sense that we have these programs but part of the program is sequential and namely we want to switch on the speaker before we play song and part of the program is parallel we can switch on the hue light independently from the speaker right so how do we solve this one thing you could do is create a slightly more generic mechanism of running a program and let's create two helper methods here to illustrate what i mean let's say we have a asynchronous function that's called run sequence and what that is going to get is a bunch of functions as arguments so let's call that functions and these functions are going to be asynchronous so they need to have a type which is a weightable you can use the awaitable type for that and we don't really care what these functions are going to return so we just assume that they get any as a result and then inside this function well the sequential version is simply running a for loop and then we're awaiting the function right so this is an awaitable and let's see we can add a second function here which we'll call run parallel and this is also going to get functions which are awaitable and both of these functions return none like so and here we're using async asyncio.gather and we simply pass it the functions that we got as an argument to run parallel so you don't necessarily need to do that but it's a bit cleaner to set it up in this way so now what you can do and i'll do this for the sleep program to show you what i mean instead of defining the program like this where we're simply defining the messages we could construct a program that consists of a waitables and then that is the program that's going to be run so it's not the messages it's the awaitables here's an example of how you could do that so sleep program for example i can translate that into a bunch of awaitables like so i'll run the function so i'll call the function run parallel so by default i'm going to run things in parallel here and the first thing i'll do is surface dot send message which is an asynchronous function and pass it this message right here and then i'm going to call the server send message again and because that's going to happen in parallel i'm going to send it this message so now these two messages are being sent in parallel but what's nice is that we run parallel and run sequence both because they're asynchronous they're actually also waitables so we can nest these things so what i can do now is have another awaitable here which is run sequence and run sequence is going to get a service dot send message and that's going to flush the toilet for us and then let's do another one which is surface dot send message and we have cleaning the toilet like so this line i can delete so let's run this program and see what happens so this is the wake up so what you see here is that we get three messages that are being sent in parallel to the hue light the smart speaker and the smart toilet they each receive these three messages and then finally smart toilet handles another message type clean and then also receive that message so this message here waits until this message is finished and that's because run sequence runs these things well in sequence so if you look again in this program we have this awaitable this awaitable and this awaitable and these are all run in parallel so that's what you see here so these three are started in parallel and including this sequence which is started in parallel which in turn sends this message as the first step that's what you see here then each of these messages has a delay so that calls sleep in this case but normally you wait for the connection to be established or the mess to be received and then we wait until the response right that's what happened here and then once this thing sends the response back of smart toilet received message then the run sequence goes to the next thing which is this right so what you could do now for example is add let's say another thing here let's say once we've cleaned flushed and cleaned the toilet in order which is great we want to switch on the speaker again and play some music because now we're very happy that the toilet is so nice and clean so what you can do now is let's say we have another run sequence call and there we're going to send a message that the speaker should switch on like so and then let's also play a song service dot send message like so so now this is the program we have these things are run in sequence and you can basically create any hierarchy of parallel and sequential function calls in this way and that's exactly what the power is of async io now there's a bunch of other stuff you can do with async io but this kind of thing already covers in my case at least 90 of what i need to do with async io just waiting for stuff and being able to run a couple of things in parallel there's other things you can do as well with async you like handling streams asynchronously dealing with sub processors queues etc etc i won't dive into that in this video but there's a lot that's possible if you're interested in this i could do a follow-up video where i dive more into those details if you like let me know in the comments if you want to see that so hope you enjoyed this quick introduction to asynchronous operations in python if you did give this video a like consider subscribing if you want to watch more of my content now you notice that here in the code i'm registering devices at the surface and this is all done in the code itself there is also a way to do it using plugins which allows you to create new devices on the fly without actually changing anything in the main code if you want to see how that works check out this video that i did a while ago where i talk about that in detail thanks for watching take care and see you next week
Info
Channel: ArjanCodes
Views: 4,586
Rating: undefined out of 5
Keywords: asynchronous, asynchronous programming, async await python, python asyncio, async python, python await, python asyncio tutorial, await python, python async await, asyncio python tutorial, asyncio python, synchronous programming, async await python explained, async await python version, python asyncio vs threading, python asyncio gather, async python explained, async python class, python async vs threading, python programming, python tutorial, Python asyncio explained
Id: 2IW-ZEui4h4
Channel Id: undefined
Length: 23min 9sec (1389 seconds)
Published: Fri Dec 17 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.