There's an important difference between how the async iteration constructs work in JavaScript (for await) and Python (async for), the ability (or lack of) to use it also to iterate a synchronous iterable/iterator.
Based on the idea that I mentioned in my previous post, the sloppy promise semantics, in JavaScript a for await loop can iterate both an asynchronous and a synchronous iterable. This is pretty coherent with the fact that we can also await for a non Promise value. The Iteration Protocols dictate that an Iterable object must have a method available with the Symbol.iterator key, that returns an Iterator object. This Iterator object must have a next method. For an Async Iterable we must have a Symbol.asyncIterator method, and the returned Async Iterator must have also a next method that returns a Promise.
A for await tries first to obtain an Async iterable checking if the object has a Symbol.asyncIterable, if that's not the case, it'll try to obtain a normal Iterator checking if the object has a Symbol.iterator. Then, as both iterables and asyncIterables have a next method, and as we know awaiting for a non awaitable value is perfectly fine, the Polymorphic behaviour is all set. Beautiful!.
let cities = ["Toulouse", "Lyon", "Xixon"];
async function* asyncCities(){
for (let city of cities){
yield await new Promise(res => setTimeout(() => res(city), 700));
}
}
async function print(items){
for await (let it of items){
console.log(it.toUpperCase());
}
}
(async () => {
await print(cities);
console.log("------");
await print(asyncCities());
})();
That's not the case in Python. An async for will try to obtain an async iterator through the aiter() function (that invokes the __aiter__ method in the object). If the object lacks that __aiter__ method, we get a:
#TypeError: 'async for' requires an object with an __aiter__ method.
No attempt is done to invoke iter/__iter__. So writing code that supports both sync and async iterables is not so elegant:
import asyncio
import time
async def getCitiesAsync():
print('getCitiesAsync')
await asyncio.sleep(0.5)
yield "Xixon"
await asyncio.sleep(0.5)
yield "Toulouse"
await asyncio.sleep(0.5)
yield "Paris"
def getCities():
print('getCities')
yield "Xixon"
yield "Toulouse"
yield "Paris"
async def printCities(cities):
if hasattr(cities, "__aiter__"):
async for city in cities:
print(city)
else:
for city in cities:
print(city)
async def main():
await printCities(getCitiesAsync())
No comments:
Post a Comment