Re-using deferred objects in Twisted

前提是你 提交于 2019-11-29 16:26:07

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 yielding 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
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!