Как не ждать в цикле с асинчио?

Вот пример игрушек, который загружает домашнюю страницу с нескольких сайтов, используя asyncio и aiohttp:

import asyncio
import aiohttp

sites = [
    "http://google.com",
    "http://reddit.com",
    "http://wikipedia.com",
    "http://afpy.org",
    "http://httpbin.org",
    "http://stackoverflow.com",
    "http://reddit.com"
]


async def main(sites):
    for site in sites:
        download(site)


async def download(site):
    response = await client.get(site)
    content = await response.read()
    print(site, len(content))


loop = asyncio.get_event_loop()
client = aiohttp.ClientSession(loop=loop)
content = loop.run_until_complete(main(sites))
client.close()

Если я запустил его, я получаю:

RuntimeWarning: coroutine 'download' was never awaited

Но я не хочу его ждать.

В скрученном я могу сделать:

for site in sites:
    download(site)

И если я явно не "уступаю" или не добавляю обратный вызов возвращенному "Отложенному", он просто запускается без блокировки и жалобы. Я не могу получить доступ к результату, но в этом случае мне это не нужно.

В JS я могу сделать:

site.forEarch(site){
    donwload(site)
}

И снова он не блокирует и не требует чего-либо с моей стороны.

Я нашел способ сделать:

async def main(sites):
    await asyncio.wait([download(site) for site in sites])

Но:

  • Это действительно не очевидно, чтобы это выяснить. Мне трудно запомнить.
  • трудно понять, что он делает. "Ожидает", кажется, говорит "я блокирую", но не ясно передает блок для завершения всего списка сопрограммы.
  • Вы не можете пройти в генератор, он должен быть реальным списком, который я считаю действительно неестественным в Python.
  • что, если я буду только ОДНО?
  • Что делать, если я не хочу вообще останавливаться на своих задачах и просто планировать их для выполнения, а затем продолжить с остальной частью моего кода?
  • это еще более подробное решение, связанное с скруткой и JS.

Это лучший способ?

Ответ 1

  • Это действительно не очевидно, чтобы это выяснить. Мне трудно запомнить.

Документация на сопрограммы делает довольно понятным, что asyncio.wait цель.

  • трудно понять, что он делает. "Ожидает", кажется, говорит "я блокирую", но не ясно передает блок для завершения всего списка сопрограммы.

Опять же, см. документацию.

  • Вы не можете пройти в генератор, он должен быть реальным списком, который я считаю действительно неестественным в Python.

Опять же, см. документацию, в частности asyncio.as_completed

  • Что делать, если у меня есть только ОДИН, ожидаемый?

Он должен работать.

  • Что делать, если я не хочу вообще останавливаться на своих задачах и просто планировать их для выполнения, а затем продолжить с остальной частью моего кода?

Затем вы можете использовать asyncio.ensure_furture. Фактически, asyncio.wait является удобной функцией вокруг asyncio.ensure_future (и некоторой другой логики).

  • это еще более подробное решение, связанное с скруткой и JS.

Может быть, но это не плохо (с моей точки зрения).

Ответ 2

Чтобы запланировать сопрограммы как задачу, используйте asyncio.ensure_future:

for site in sites:
    coro = download(site)
    future = asyncio.ensure_future(coro)

Он заменяет устаревшую функцию asyncio.async в версии 3.4.4.

Затем вы можете управлять этими фьючерсами с помощью await, asyncio.wait или asyncio.gather.