Issue
Take these two following functions:
import asyncio
def a():
for index in range(2):
# Capture `index` as `local_index` in case awaiting is postponed.
async def next_index(local_index=index):
# Simulate network request.
await asyncio.sleep(0)
return local_index
yield next_index()
async def b():
for index in range(2):
# Simulate network request.
await asyncio.sleep(0)
yield index
a
returns an Iterable[Awaitable[int]]
. b
returns an AsyncIterable[int]
. Iteration for both can be done like so:
async def main():
for index in a():
print(await index)
async for index in b():
print(index)
asyncio.run(main())
Output:
0
1
0
1
The key thing from the above example is that I was able to yield Awaitable
s without an outer async
because the inner function is async
.
- Is there anything an
AsyncIterable[T]
allows over anIterable[Awaitable[T]]
in terms of functionality?
I also have a very related question. From PEP 492 - Asynchronous Iterators and "async for":
An asynchronous iterator object must implement an anext method (or, if defined with CPython C API, tp_as_async.am_anext slot) returning an awaitable.
- Because an outer
async
was not required to yieldAwaitable
s, does__anext__
offer exclusive functionality over a synchronous__next__
returningAwaitable
s?
This is where I'm probably missing something, but from my current understanding, the asynchronous protocol and StopAsyncIteration
look like they can be mimicked using the synchronous protocol and StopIteration
(with less brevity of course).
Solution
One big issue you're missing is how the end of the loop is handled.
An asynchronous iterator's __anext__
returns an awaitable, which can suspend, raise StopIteration
with a value to produce the next element, or raise StopAsyncIteration
to signal the end of the loop. (The PEP says "To stop iteration __anext__
must raise a StopAsyncIteration exception.", but the StopAsyncIteration exception really happens when the awaitable is awaited, not synchronously when __anext__
is called.)
In contrast, if you try to make a regular iterable with awaitable elements, then the iterator's __next__
needs to raise StopIteration
to end the loop.
This means that __next__
can't return until it knows whether or not there will be another element. __next__
is synchronous, so while it's figuring this out, control cannot return to the event loop. This means you might waste a bunch of time synchronously waiting for network traffic or something while all other work stalls.
You can work around this with even more manual handling, but it gets really awkward on both ends, especially the iterator's end, and you need some sort of equivalent of StopAsyncIteration to disambiguate "here's the next element" from "loop's done".
Answered By - user2357112 supports Monica
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.