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
— это просто ярлык для получения результата блокирующей функции в качестве отложенного, к которому вы затем можете добавить обратные вызовы/ошибки. Обратные вызовы будут запущены с обычной семантикой цепочки. Это кажется немного сумасшедшим, но Deferred'ы могут «срабатывать» до того, как вы добавите обратные вызовы, и обратные вызовы все равно будут вызываться.
Итак, отвечая на ваш вопрос, почему это полезно? Что ж, 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
person
Crast
schedule
10.09.2010