Join us and get access to thousands of tutorials and a community of expert Pythonistas.

Unlock This Lesson

This lesson is for members only. Join us and get access to thousands of tutorials and a community of expert Pythonistas.

Unlock This Lesson

asyncio.sleep() and Writing Your First Coroutine

In this lesson, you’ll learn how to convert a normal synchronous function into an asynchronous coroutine.

from random import randint
import time
import asyncio

async def randn():
   await asyncio.sleep(3)
    return randint(1, 10)

00:00 Okay, let’s go ahead and turn this synchronous version of randn() to an asynchronous version, and in the process turn it into a coroutine. The first thing we can do is change time.sleep() to asyncio.sleep().

00:20 This is going to turn this from the old version, which was synchronous, and now this is asynchronous. Now, you’re probably wondering what is that going to do? Well, it’s going to make it a lot faster. And how’s it going to do it?

00:30 I’ll show that in just a minute. Let’s go ahead and go to IPython and see if this will run.

00:40 So, here’s IPython, and if I do randn()—remember, last time it waited 3 seconds and then it spit out a value. Now it will instantly give me an error message saying the 'sleep' thing was never awaited.

00:52 Now, you’ll notice here that it says it’s a coroutine, so this function went from a normal function, now Python is telling us, “Oh, this thing is actually a coroutine now.” And it says 'sleep' was never awaited. What does this error message mean?

01:06 Well, anytime you do something that’s asynchronous, you need to wait for it, and in Python we call that await. Now, you might think, “Does this thing mean the same as before?” Like, when you did time.sleep(), it’s going to wait 3 seconds, it’s going to sleep, and wait 3 seconds. This time, when you do await, it actually doesn’t wait. Well, let me actually rewind that a little bit.

01:32 What this means is any code that happens after this function will not happen until this happens. So line 12 has to happen first in order for line 13 to happen.

01:41 But I could be down here in main() and be doing other things, and those other things would still be running. So this await allows this particular function to pause, but other things in Python that are still quote-unquote “running” can still run, okay? While this is running, you could imagine it running in the background until it’s finished.

02:05 In that case, the system says, “Hey, I’m done sleeping,” and then it notifies us that the sleeping has finished, and then after it notifies us then line 13 executes. Okay.

02:17 So, once again, I got this error message down here that says 'sleep' was not awaited. Okay, so I did an await on that. What happens if I save this, and I flip back and try it again? I’m going to exit out

02:34 and then run—oh, okay. We automatically got an error message. It says await happened outside of an asynchronous function. That’s another point, is anytime you do an await inside of a function like this, it cannot be a normal function—it has to be an asynchronous function.

02:49 And how you change a function from normal into asynchronous is you say async.

02:57 So, this function is asynchronous, and inside of asynchronous functions, you have to use the await keyword and you have to do an await on something that happens, like, in the background, for example like writing to a disk, writing to a database, calling a website—something that happens outside of Python’s process.

03:18 Like, things considered IO, or input/output. Okay, so we’re going to wait 3 seconds and now let’s try it. So I’m going to save this, come back here.

03:33 No error message, that’s good. And if I do randn()

03:38 I got no error messages, that’s great. And now it says now I have not a generator, like I got before, but now it says you have a <coroutine object>, right? The other one was odds(), let’s just show that. odds(3, 11).

03:53 So that was a generator, which is something that produces values. This randn() is a coroutine—you could think of it as consuming values. It’s more of a consumer, whereas a generator is more like a producer. Okay.

04:06 But both of these things, generators and coroutines, allow you to pause execution at any time. Okay, so let’s exit out of this. Now, you may think “I have this coroutine, but how do I actually run it?” Well, how you run a coroutine is a little bit strange and it involves something called an event loop.

04:27 This is how the event loop works. What I’m going to do is, I need to call randn() inside of main(), so I’m going to say—because randn() is asynchronous itself, I can’t just say, like, random value is equal to randn().

04:50 You may think that, “Oh, I’m going to call randn(), here it is, and its return value, this random number, will go into r.” Right?

04:57 You may think that, and then let’s go back here and just see what happens if I try to do that—python

05:06 And it says coroutine 'randn' was never awaited. So once you have something that’s asynchronous, you always have to use await. Okay, so because asyncio.sleep() was asynchronous, you had to do await. Because randn() is asynchronous, you also have to await it as well, too.

05:27 Okay? So, let’s try that. I come back here and run this again, and it says, “Now you’re doing an await outside of an asynchronous function.” So now, this main() cannot be a regular function—it also has to turn into an asynchronous function.

05:45 And let’s print out rprint('r:', r).

Avatar image for Vaibhav Chauhan

Vaibhav Chauhan on April 21, 2019

so what’s the event loop in this example ?

Avatar image for UBBA

UBBA on May 16, 2019

No event loop in this lesson. Next lesson.

Avatar image for Pygator

Pygator on Sept. 14, 2019

Don’t understand how a coroutine is a consumer? It produced a random number here. I see how generators are produces, because they “generate” sequences one value at a time.

Avatar image for fofanovis

fofanovis on April 9, 2020

Dont understand how a coroutine is a consumer? It produced a random number here. I see how generators are produces, because they generate sequences one value at a time.

If you watch more, you’ll see that they consume output from async funcs using await. In that sense, they are consumers.

Become a Member to join the conversation.