1

With this asyncio code, my tutor says that all coroutines (here the 50 "fetch_page") first stop at the first async with and wait, then all of them resume from there and stop at the second async with, then finally all of them all return.

import aiohttp import asyncio async def fetch_page(url): print(1) async with aiohttp.ClientSession() as session: print(2) async with session.get(url) as response: print(3) return response.status loop = asyncio.get_event_loop() tasks = [fetch_page('http://google.com') for i in range(50)] loop.run_until_complete(asyncio.gather(*tasks)) 

I'm debugging this, and I must say he's wrong. While debugging, I see all coroutines going sequentially to the second async with, where they all stop. Then once all the 50 coroutines resumes, they do the session.get(url) and return.

But why not all of the couroutines stop at the first async with ?

Print output : "1 2 1 2 1 2 ... 3 3 3 ...", instead of "1 1 1 ... 2 2 2 ... 3 3 3 ..."

2 Answers 2

2

But why not all of the couroutines stop at the first async with?

Just like any other await, an async with doesn't guarantee that the coroutine will suspend there, but it allows the coroutine to suspend if there is reason to do so. In this case, merely creating a ClientSession is an operation that can be done without suspending any coroutine, so each of them simply proceeds further.

Entering the second async with must obtain a response object, which requires that the request has been sent and the HTTP headers received. Executed on a non-blocking socket, some of these operations will signal that the data is not immediately available (because it needs to reach the server, and the server has to craft a response, and then the response has to travel back to us), and asyncio handles that by suspending the coroutine. This is why the second async with is practically guaranteed to suspend each coroutine that reaches it.

Sign up to request clarification or add additional context in comments.

Comments

1

There is a little piece of info is missing here:

asyncio is a single-threaded library based on event loop.

What happens in this case is that aiohttp.ClientSession() as session is not an I/O expensive operation, it doesn't take time to execute, that's why it gets executed immediately even before the iterator gets to the next loop cycle.

Why 3333 get's printed last? because you're making an I/O that takes time, more than looping for 50 times.

For more details read here: asyncio python doc

3 Comments

Great ! Am I right in saying this : each coroutine do their job, without stopping, but the async/await is permitting the suspending/resuming of execution so that other coroutine can enter doing their job. This is not multithreading, it's all on the main thread.
@trogne all threads are suspect to suspending/resuming when they require more than certain period of time determined by operating system task scheduler. so yes async/await will make the "asynchronous" section of your code suspect to being suspended.
Coroutines do not "delegate to other thread", asyncio is single-threaded.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.