defer.execute は確かに、同じスレッドでブロッキング方式で関数を実行します。関数fが例外をスローした場合にエラーバックが発生したコールバックを返すことを除いてdefer.execute(f, args, kwargs)
、同じことを行うという点で正しいです。一方、 defer.succeed の例では、関数が例外をスローした場合、それは外側に伝播するため、望ましくない場合があります。defer.succeed(f(*args, **kwargs))
defer.execute
わかりやすくするために、defer.execute のソースをここに貼り付けます。
def execute(callable, *args, **kw):
"""Create a deferred from a callable and arguments.
Call the given function with the given arguments. Return a deferred which
has been fired with its callback as the result of that invocation or its
errback with a Failure for the exception thrown.
"""
try:
result = callable(*args, **kw)
except:
return fail()
else:
return succeed(result)
つまり、defer.execute
ブロッキング関数の結果を deferred として受け取るためのショートカットにすぎず、これにコールバック/エラーバックを追加できます。コールバックは、通常のチェーン セマンティクスで起動されます。少しクレイジーに思えますが、Deferred はコールバックを追加する前に「起動」することができ、コールバックは引き続き呼び出されます。
あなたの質問に答えるために、なぜこれが役立つのですか?まあ、defer.execute
テスト/モック化と、非同期APIを同期コードと単に統合するのに役立ちます.
defer.maybeDeferred
関数を呼び出して、関数がすでに deferred を返している場合は単純にそれを返し、そうでない場合は と同様の関数も役立ちますdefer.execute
。これは、呼び出されたときに deferred を提供する callable を期待する API を作成し、通常のブロッキング関数も受け入れられるようにする場合に役立ちます。
たとえば、ページを取得して処理するアプリケーションがあるとします。そして、なんらかの理由で、シングル ショットの crontab スクリプトのように、または WSGI アプリケーションでの要求に応答して、特定のユース ケースでこれを同期方式で実行する必要がありましたが、それでも同じコードベースを保持しています。コードが次のようになっている場合は、次のように実行できます。
from twisted.internet import defer
from twisted.web.client import getPage
def process_feed(url, getter=getPage):
d = defer.maybeDeferred(getter, url)
d.addCallback(_process_feed)
def _process_feed(result):
pass # do something with result here
これをリアクターなしで同期コンテキストで実行するには、次のように別のゲッター関数を渡すだけです。
from urllib2 import urlopen
def synchronous_getter(url):
resp = urlopen(url)
result = resp.read()
resp.close()
return result