defer.execute действительно выполняет функцию блокирующим образом, в том же потоке, и вы правы в том, что defer.execute(f, args, kwargs)
делает то же самое, что и defer.succeed(f(*args, **kwargs))
, за исключением , что defer.execute
возвращаетобратный вызов, который вызвал ошибку, если функция f выдает исключение.Между тем, в вашем примере defer.succeed, если функция выдает исключение, она будет распространяться наружу, что может быть нежелательно.
Для простоты понимания я просто вставлю сюда источник 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
- это просто ярлык для получения результата функции блокировки в качестве отложенного, к которому можно затем добавить обратные вызовы / ошибки.Обратные вызовы будут выполняться с нормальной семантикой цепочки.Это кажется немного сумасшедшим, но Deferreds может «выстрелить» до того, как вы добавите обратные вызовы, и обратные вызовы все равно будут вызываться.
Итак, чтобы ответить на ваш вопрос, почему это полезно ?Что ж, defer.execute
полезен как для тестирования / проверки, так и для простой интеграции асинхронного API с синхронным кодом.
Также полезным является defer.maybeDeferred
, который вызывает функцию, а затем, если функция уже возвращает deferred, просто возвращает ее, в противном случае функции аналогичны defer.execute
.Это полезно, когда вы пишете 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