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