Python 3, an Illustrated Tour Transcripts
Chapter: Asynchronous Programming
Lecture: Walk-through: Asyncio Context Iterators

Login or purchase this course to watch this video and the rest of the course contents.
0:00 In this video, we're going to look at async iter test, open that up in your editor. The first part says write an asynchronous iterator countdown
0:07 that accepts a count and a delay, when looped over asynchronously, it returns the numbers from count down to and including zero.
0:15 It waits for delay seconds before returning the next value. Okay, so we need to define a class called count down
0:23 and it needs to have a constructor that has a count and a delay so def __init__ and it needs to have a count and a delay in here
0:36 self.count is equal to count and self.delay is equal to delay. Okay, we need to implement this asynchronous iteration protocol.
0:46 So the first one is def __aiter__, and this can be defined as asynchronous or not, it just depends on whether you want to do an asynchronous call
0:57 in this case, I don't want to, I'm just going to return self and make this a self iterating coroutine.
1:03 Now, I need to define a __anext__ and this does need to be an async call so def __anext__.
1:20 Okay, there's a __anext__ and inside of here, we want to return count and then delay after each count.
1:29 So we need to have some little logic there to say something like, maybe I need to come up here and keep track of the value
1:35 that I'm going to return next. I'm going to say self.value is equal to count, value is what I'm going to return.
1:41 If self.val is equal to self.count, then let's just return self.val. So the first time we don't want to delay before we return the value,
1:55 so we want to say 10 and then wait for a second or whatever and then say 9 and then wait for a second and keep going that way.
2:01 Now, in this case, if self.val what we're going to return is equal to 0, we also want to return self.val
2:21 and otherwise, we want to say we want to sleep for delay and decrement our self.val. So we want to say await asyncio.sleep for self.delay
2:38 and then we want to return self.val and we're going to say self.val minus equals 1 and return self.val.
2:58 Okay, let's try this see if it works here. I'm going to say run async test, it thinks for a minute here and I get an error on line 43.
3:14 So that's this guy right here, I got an assertion error, so down here we're basically unrolling this protocol here.
3:21 We're saying get the aiter and then get a start time and call next on it and the first value since we passed in 2 should be 2
3:31 and assert the time is less than half a second since we're saying delay of 1 and then we're getting the next and saying that
3:40 the next value should be 1 and we got the next value was 2 instead of 1, so let's go up here and look at our logic here.
3:48 So the first time we returned self.val we didn't do anything, so our self.val is just going to still be self.val.
3:58 So maybe I want to say something like this like val is equal to self.val and if val is equal to self.count, return val.
4:09 And at this point we're going to say self.val minus equals 1 up here. And let's see if that works a little bit better.
4:25 So in the first case, we'll say val is equal to self.val which should be the start value.
4:32 We're going to decrement our instance member which shouldn't affect val and then if we're starting we're just going to return,
4:39 if we're at the end we're going to return. I think this is wrong, we don't want to return at the end,
4:45 we want to sleep before that, so we'll just get rid of that and we'll say if val is less than 0 then we want to raise a stop async iteration.
5:05 So that says we are done once we get 0 so don't do any more sleeping or whatever. Let's run this and see if it works.
5:18 Okay, I got an asyncio is not defined here. I better fix that and make it defined, import asyncio, let's run it again.
5:35 And it looks like it worked that time. So it passed, note that it took 2 seconds to run, or a little bit more than 2 seconds,
5:41 which makes sense because I said I want to count down from 2 and I want to have a one second delay in between there.
5:48 So it should give me 2, wait for one second, give me 1, wait for a second, give me 0 and not wait after that.
5:54 Note that the test here, we keep calling next and we assert it, it raises that stop async iteration error. So this is a little bit trickier.
6:05 There's some logic in here that you've got to sort of figure out but once you've got it, you can see that you have a little asynchronous counter
6:13 that will count down and sleep in there. Again, note that this __anext__, this is a coroutine
6:21 and because it's defined with async we can call await in there. This gives anything else on the event loop that wants to run
6:28 a chance to run at that point in time.

Talk Python's Mastodon Michael Kennedy's Mastodon