本文介绍了学习异步:“从未等待协程”警告错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试学习在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")

我使用了新的(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.

接下来,您需要移动结束的)表达式上的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.

这篇关于学习异步:“从未等待协程”警告错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-16 06:49