我有一个非常接近此的代码:
class Parser:
async def fetch(self, url):
html = await get(url)
return html
@property
def endless_generator(self):
while True:
yield url
async def loop():
htmls = []
for url in self.endless_generator:
htmls.append(await self.fetch(url))
return htmls
def run(self):
loop = asyncio.get_event_loop()
try:
htmls = loop.run_until_complete(self.loop())
finally:
loop.close()
parser = Parser()
parser.run()
现在Parser.loop
同步运行。
我已经尝试asyncio.wait
和asyncio.gather
来实现Parser.fetch
的异步调用,但是我不预先知道URL的数量(因为URL是由无尽的生成器产生的)。
那么,如果事先不知道任务数,如何获得异步调用?
答案 0 :(得分:2)
我已经尝试
asyncio.wait
和asyncio.gather
来实现Parser.fetch
的异步调用,但是我不预先知道URL的数量(因为URL是由无尽的生成器产生的)。
我认为无限生成器是指事先未知URL数量的生成器,而不是真正的无限生成器(生成无限列表)。这是一个版本,可在URL可用时立即创建任务,并在结果到达时收集结果:
async def loop():
lp = asyncio.get_event_loop()
tasks = set()
result = {}
any_done = asyncio.Event()
def _task_done(t):
tasks.remove(t)
any_done.set()
result[t.fetch_url] = t.result()
for url in self.endless_generator:
new_task = lp.create_task(self.fetch(url))
new_task.fetch_url = url
tasks.add(new_task)
new_task.add_done_callback(_task_done)
await any_done.wait()
any_done.clear()
while tasks:
await any_done.wait()
any_done.clear()
return result # mapping url -> html
一个人不能在每次迭代中简单地调用gather
或wait
,因为那样将等待所有现有任务完成,然后再排队。 wait(return_when=FIRST_COMPLETED)
可以工作,但任务数量为O(n**2)
,因为它每次都会重新设置自己的回调。