学习异步:“从未等待协程”警告错误 [英] Learning asyncio: "coroutine was never awaited" warning error
问题描述
我正在尝试学习在Python中使用asyncio优化脚本。
我的示例返回了一个从未等待过的协程警告,您能否帮助理解并找到解决方法?
I am trying to learn to use asyncio in Python to optimize scripts.
My example returns a coroutine was never awaited
warning, can you help to understand and find how to solve it?
import time
import datetime
import random
import asyncio
import aiohttp
import requests
def requete_bloquante(num):
print(f'Get {num}')
uid = requests.get("https://httpbin.org/uuid").json()['uuid']
print(f"Res {num}: {uid}")
def faire_toutes_les_requetes():
for x in range(10):
requete_bloquante(x)
print("Bloquant : ")
start = datetime.datetime.now()
faire_toutes_les_requetes()
exec_time = (datetime.datetime.now() - start).seconds
print(f"Pour faire 10 requêtes, ça prend {exec_time}s\n")
async def requete_sans_bloquer(num, session):
print(f'Get {num}')
async with session.get("https://httpbin.org/uuid") as response:
uid = (await response.json()['uuid'])
print(f"Res {num}: {uid}")
async def faire_toutes_les_requetes_sans_bloquer():
loop = asyncio.get_event_loop()
with aiohttp.ClientSession() as session:
futures = [requete_sans_bloquer(x, session) for x in range(10)]
loop.run_until_complete(asyncio.gather(*futures))
loop.close()
print("Fin de la boucle !")
print("Non bloquant : ")
start = datetime.datetime.now()
faire_toutes_les_requetes_sans_bloquer()
exec_time = (datetime.datetime.now() - start).seconds
print(f"Pour faire 10 requêtes, ça prend {exec_time}s\n")
代码的第一个 classic 部分正确运行,但下半部分仅产生:
The first classic part of the code runs correctly, but the second half only produces:
synchronicite.py:43: RuntimeWarning: coroutine 'faire_toutes_les_requetes_sans_bloquer' was never awaited
推荐答案
您制作了 faire_toutes_les_requetes_sans_bloquer
使用 async def
作为 awaitable 函数的协程。
You made faire_toutes_les_requetes_sans_bloquer
an awaitable function, a coroutine, by usingasync def
.
何时你叫一个等待函数,您将创建一个新的协程对象。该函数中的代码要等到您在函数上 await 或将其作为任务运行后才能运行:
When you call an awaitable function, you create a new coroutine object. The code inside the function won't run until you then await on the function or run it as a task:
>>> async def foo():
... print("Running the foo coroutine")
...
>>> foo()
<coroutine object foo at 0x10b186348>
>>> import asyncio
>>> asyncio.run(foo())
Running the foo coroutine
函数 synchronous ,因为您直到在该函数内部才开始循环:
You want to keep that function synchronous, because you don't start the loop until inside that function:
def faire_toutes_les_requetes_sans_bloquer():
loop = asyncio.get_event_loop()
# ...
loop.close()
print("Fin de la boucle !")
但是,您还尝试使用 aiophttp.ClientSession()
对象,并且这是一个异步上下文管理器,您应该将其与 async with
async一起使用,而不仅仅是 with
,因此必须在等待任务中运行。如果您将与
一起使用,而不是与
进行异步,则 TypeError(使用异步与代替)
将引发异常。
However, you are also trying to use a aiophttp.ClientSession()
object, and that's an asynchronous context manager, you are expected to use it with async with
, not just with
, and so has to be run in aside an awaitable task. If you use with
instead of async with
a TypeError("Use async with instead")
exception will be raised.
这都意味着您需要移动 loop.run_until_complete()
调用 faire_toutes_les_requetes_sans_bloquer()
函数的 out ,因此您可以将其作为要运行的主要任务;您可以直接在 asycio.gather()
上呼叫并等待,然后:
That all means you need to move the loop.run_until_complete()
call out of your faire_toutes_les_requetes_sans_bloquer()
function, so you can keep that as the main task to be run; you can call and await on asycio.gather()
directly then:
async def faire_toutes_les_requetes_sans_bloquer():
async with aiohttp.ClientSession() as session:
futures = [requete_sans_bloquer(x, session) for x in range(10)]
await asyncio.gather(*futures)
print("Fin de la boucle !")
print("Non bloquant : ")
start = datetime.datetime.now()
loop.run(faire_toutes_les_requetes_sans_bloquer())
exec_time = (datetime.datetime.now() - start).seconds
print(f"Pour faire 10 requêtes, ça prend {exec_time}s\n")
我使用了新的 asyncio.run()
函数(Python 3.7及更高版本)运行单个主要任务。
I used the new asyncio.run()
function (Python 3.7 and up) to run the single main task. This creates a dedicated loop for that top-level coroutine and runs it until complete.
接下来,您需要移动结束的)$
await resp.json()
表达式上的c $ c>括号:
Next, you need to move the closing )
parenthesis on the await resp.json()
expression:
uid = (await response.json())['uuid']
您要访问的'uuid'
键是 await
的结果,而不是响应的协程。
You want to access the 'uuid'
key on the result of the await
, not the coroutine that response.json()
produces.
通过这些更改,您的代码可以工作,但是asyncio版本会在不到一秒的时间内完成;您可能需要打印微秒:
With those changes your code works, but the asyncio version finishes in sub-second time; you may want to print microseconds:
exec_time = (datetime.datetime.now() - start).total_seconds()
print(f"Pour faire 10 requêtes, ça prend {exec_time:.3f}s\n")
在我的计算机上,同步的请求
代码在大约4-5秒内完成,而asycio代码在0.5秒内完成。
On my machine, the synchronous requests
code in about 4-5 seconds, and the asycio code completes in under .5 seconds.
这篇关于学习异步:“从未等待协程”警告错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!