defer.execute rzeczywiście wykonać funkcję blokowania w sposób, w tym samym wątku i masz rację, że defer.execute(f, args, kwargs)
działa tak samo jak defer.succeed(f(*args, **kwargs))
wyjątkiem że defer.execute
zwróci zwrotnego, który miał errback zwolniony jeśli funkcja f zgłasza wyjątek. W międzyczasie, w przykładzie odroczenia. Jeśli funkcja wyrzuci wyjątek, będzie propagować na zewnątrz, co może nie być pożądane.
Dla ułatwienia zrozumienia, po prostu wklej źródło defer.execute tutaj:
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)
Innymi słowy, defer.execute
to tylko skrót do podjęcia wynik funkcji blokowania jako odroczone, które można następnie dodaj callbacks/errbacks do. Callbacks będą uruchamiane z normalną semantyką łączenia. Wydaje się to trochę szalone, ale Deferreds może "strzelać", zanim dodasz callbacka, a callbacki będą nadal wywoływane.
Tak, aby odpowiedzieć na pytanie, dlaczego jest to przydatna? Cóż, defer.execute
jest przydatny zarówno do testowania/szyderstwa, jak i prostej integracji api async z kodem synchronicznym.
Użyteczne jest także defer.maybeDeferred
, które wywołuje funkcję, a następnie, jeśli funkcja już zwraca, odroczone po prostu ją zwraca, w przeciwnym razie działa podobnie do defer.execute
. Jest to przydatne, gdy piszesz API, które oczekuje wywołania, które po wywołaniu daje odroczone, i chcesz móc również akceptować normalne funkcje blokowania.
Załóżmy na przykład, że masz aplikację, która pobrała strony i zrobiła z nią coś. I, z jakiegoś powodu, trzeba było uruchomić to w sposób synchroniczny dla konkretnego przypadku użycia, jak w pojedynczym skrypcie crontab lub w odpowiedzi na żądanie w aplikacji WSGI, ale nadal zachowywać ten sam kod. Jeśli kod wyglądał tak, można to zrobić:
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
Aby uruchomić ten w synchronicznym kontekście bez reaktora, można po prostu przejść alternatywną funkcję pobierającą, tak:
from urllib2 import urlopen
def synchronous_getter(url):
resp = urlopen(url)
result = resp.read()
resp.close()
return result
+ 1 dla wyjaśnienia defer.maybeDeferred –