Use the first one :. You not only explicitly show the places where the code can be paused (where await is placed), but also get all the benefits associated with it, such as tracebacks, which show a useful thread of execution
To see the difference, modify the deep coroutine to cause an error:
async def deep(time): await asyncio.sleep(time) raise ValueError('some error happened') return time
For the first fragment, you will see this output:
Traceback (most recent call last): File ".\tmp.py", line 116, in <module> loop.run_until_complete(test()) File ".\Python36\lib\asyncio\base_events.py", line 466, in run_until_complete return future.result() File ".\tmp.py", line 113, in test print(await a(0.1)) File ".\tmp.py", line 110, in a return await b(time) File ".\tmp.py", line 106, in b return await c(time) File ".\tmp.py", line 102, in c return await deep(time) File ".\tmp.py", line 97, in deep raise ValueError('some error happened') ValueError: some error happened
But only for the second fragment:
Traceback (most recent call last): File ".\tmp.py", line 149, in <module> loop.run_until_complete(test()) File ".\Python36\lib\asyncio\base_events.py", line 466, in run_until_complete return future.result() File ".\tmp.py", line 146, in test print(await a(0.1)) File ".\tmp.py", line 130, in deep raise ValueError('some error happened') ValueError: some error happened
As you can see, the first trace helps you see the "real" (and useful) thread of execution, and the second does not.
The first way to write code is also much better to support: imagine that you immediately realized that b(time) should also contain some asynchronous call, for example, await asyncio.sleep(time) . In the first code snippet this call can be placed directly without any other changes, but the second you will have to rewrite many parts of your code.