Reputation: 1382
I've been having a hard time understanding Python's asyncio module and how to not block on asynchronous calls. For instance, given this code snippet:
import aiohttp
import asyncio
import async_timeout
async def fetch(session, url):
with async_timeout.timeout(10):
async with session.get(url) as response:
return await response.text()
async def main(loop):
print(1)
async with aiohttp.ClientSession(loop=loop) as session:
html = await fetch(session, 'http://python.org')
print(html)
print(2)
loop = asyncio.get_event_loop()
loop.run_until_complete(main(loop))
I would expect, similar to how this would work in Javascript, the output to be
1
2
<!doctype html>
<...>
Instead, the function prints 1, blocks until it gets back the html, then prints 2. Why does it block, and how / can I avoid the blocking? Thanks for your help.
Upvotes: 2
Views: 1036
Reputation: 31
To understanding asyncio in python 3.6 async def
style I suggest you to run this simple example:
import asyncio
import time
def write(msg):
print(msg, flush=True)
async def say1():
await asyncio.sleep(1)
write("Hello 1!")
async def say2():
await asyncio.sleep(1)
write("Hello 2!")
write("start")
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.gather(
say1(),
say2()
))
write("exit")
loop.close()
Source: https://maketips.net/tip/146/parallel-execution-of-asyncio-functions See link for detailed explanations of this code with call diagram .
Upvotes: 3
Reputation: 701
Your problem is this line:
html = await fetch(session, 'http://python.org')
this means - wait until getting a response from fetch, Or in other word's it's blocking the current coroutine until the fetch coroutine return's the response text.
To make things simpler:
Calling a coroutine does not start its code running – the coroutine object returned by the call doesn’t do anything until you schedule its execution. There are two basic ways to start it running: call await coroutine or yield from coroutine from another coroutine (assuming the other coroutine is already running!), or schedule its execution using the ensure_future() function or the AbstractEventLoop.create_task() method.
And a fix (for getting the desired output):
import asyncio
import aiohttp
import async_timeout
async def fetch(session, url):
with async_timeout.timeout(10):
async with session.get(url) as response:
return await response.text()
async def main(loop):
print(1)
async with aiohttp.ClientSession(loop=loop) as session:
future = asyncio.ensure_future(fetch(session, 'http://python.org')) # we start the fetch process
print(2)
html = await future # we wait for getting the fetch response
print(html)
loop = asyncio.get_event_loop()
loop.run_until_complete(main(loop))
Upvotes: 3