The issue is not that the Deferred
itself can only be "used once" - it's infinitely re-usable, in the sense that you can keep adding callbacks to it forever and data will continue flowing to the next callback, and the next, as it's available. The problem you're seeing is that when you add a callback to a Deferred
, its result is propagated to the next callback.
The other, intersecting problem here is that yield
ing a Deferred
from an inlineCallbacks
function is assumed to "consume" a Deferred
- you're getting its value and doing something with it, so to prevent unnecessary resource utilization (that Deferred
carrying around a result for longer than it needs to), the callback that gives you the result from the yield
expression also itself returns None
. It might be a little easier to understand if it returned some kind of more explicit "consumed by inlineCallbacks
token", I suppose, but hindsight is 20/20 :-).
But in a sense, a Deferred
can only be "used" once, which is to say, if you have an API which returns a Deferred
, it should return a new Deferred
to each caller. By returning it, you're really transferring ownership to the caller, because callers may modify the result, to pass on to their own callers. The typical example is that if you have an API that returns a Deferred
that fires with some bytes, but you know the bytes are supposed to be JSON, you might add .addCallback(json.loads)
and then return it, which would allow that caller to consume the JSON-serialized object rather than the bytes.
So if you intend for async
to be called multiple times, the way you would do it is something like this:
from __future__ import print_function, unicode_literals
from twisted.internet import defer
class Foo(object):
def __init__(self):
self.dfd = defer.Deferred()
def async(self):
justForThisCall = defer.Deferred()
def callbackForDFD(result):
justForThisCall.callback(result)
return result
self.dfd.addCallback(callbackForDFD)
return justForThisCall
@defer.inlineCallbacks
def func(self):
print('Started!')
result = yield self.async()
print('Stopped with result: {0}'.format(result))
if __name__ == '__main__':
foo = Foo()
print("calling func")
foo.func()
print("firing dfd")
foo.dfd.callback('no need to wait!')
print("calling func again")
foo.func()
print("done")
which should produce this output:
calling func
Started!
firing dfd
Stopped with result: no need to wait!
calling func again
Started!
Stopped with result: no need to wait!
done
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…