2017-01-27 6 views
1

現在、多くのHTTPリクエストを順番に実行して結果を得る非効率な同期ジェネレータがあります。 asyncioaiohttpを使用してリクエストを並列化し、このジェネレータを高速化したいのですが、通常のジェネレータ(PEP 525 async generatorではなく)にしておきたいので、非同期コードで呼び出す必要はありません変更される。どのようにしてそのような発電機を作ることができますか?コルーチンが終了するときにコルーチン結果を生成するジェネレータを作成

答えて

4

asyncio.as_completed()は、コルーチンまたは先物の繰返しを受け取り、入力先物が完了する順序で先物の繰返しを返します。 通常、あなたはその結果をループをいただきたいとawaitasync関数内からメンバーを...

import asyncio 

async def first(): 
    await asyncio.sleep(5) 
    return 'first' 

async def second(): 
    await asyncio.sleep(1) 
    return 'second' 

async def third(): 
    await asyncio.sleep(3) 
    return 'third' 

async def main(): 
    for future in asyncio.as_completed([first(), second(), third()]): 
     print(await future) 

loop = asyncio.get_event_loop() 

# Prints 'second', then 'third', then 'first' 
loop.run_until_complete(main()) 

を...しかし、この質問の目的のために、私たちが望むことは降伏することができることです通常の同期コードは、async関数がフードの下で使用されていることを知らずにそれらを消費することができるように、通常のジェネレータからの結果です。私たちは、このように

import asyncio 

async def first(): 
    await asyncio.sleep(5) 
    return 'first' 

async def second(): 
    await asyncio.sleep(1) 
    return 'second' 

async def third(): 
    await asyncio.sleep(3) 
    return 'third' 

def ordinary_generator(): 
    loop = asyncio.get_event_loop() 
    for future in asyncio.as_completed([first(), second(), third()]): 
     yield loop.run_until_complete(future) 

# Prints 'second', then 'third', then 'first' 
for element in ordinary_generator(): 
    print(element) 

は、我々は必要としない方法で非非同期の土地に私たちの非同期コードを公開しました...私たちの as_completed呼び出しによって得られた先物 loop.run_until_complete()を呼び出すことによってそれを行うことができますファンクションを asyncと定義したり、 ordinary_generatorがフードの下で asyncioを使用していることを知ることさえできます。

、いくつかの状況では、より多くの柔軟性を提供していますordinary_generator()の代替実装として、我々は代わりにas_completed()をループのFIRST_COMPLETEDフラグとasyncio.wait()を繰り返し呼び出すことができます。

import concurrent.futures 

def ordinary_generator(): 
    loop = asyncio.get_event_loop() 
    pending = [first(), second(), third()] 
    while pending: 
     done, pending = loop.run_until_complete(
      asyncio.wait(
       pending, 
       return_when=concurrent.futures.FIRST_COMPLETED 
      ) 
     ) 
     for job in done: 
      yield job.result() 

このアプローチ、pendingジョブのリストを維持し、ジョブを即座にpendingリストに追加することができるという利点があります。これは、非同期ジョブが、キューにアクセスする各ページのすべてのリンクに続くWebスパイダーのように、予想外の数のジョブをキューに追加できるような場合に便利です。

+0

これらには 'loop.close()'が必要ですか? – Neil

関連する問題