twisted deferred/callbacks and asynchronous execution

不想你离开。 提交于 2019-12-03 14:15:56

The callbacks are (by default) synchronous. However, as the Twisted doc points out:

If you need one Deferred to wait on another, all you need to do is return a Deferred from a method added to addCallbacks.

So you can use that to do some asynchronous processing in your callback chain. Let's do that:

from twisted.internet import reactor, defer

def callback_func_2(result, previous_data):
    # here we pass the result of the deferred down the callback chain
    # (done synchronously)
    print "calling function 1 on result:%s with previous result:%s" % (result, previous_data)
    return result

def callback_func(result):
    #let's do some asynchronous stuff in this callback
    # simple trick here is to return a deferred from a callback 
    # instead of the result itself.
    # 
    # so we can do asynchronous stuff here, 
    # like firing something 1 second later and have 
    # another method processing the result
    print "calling function 1 on result:%s" % result
    d = defer.Deferred()
    reactor.callLater(1, d.callback, "second callback")
    d.addCallback(callback_func_2, result)
    return d

def do():
    d = defer.Deferred()
    reactor.callLater(1, d.callback, "first callback")
    d.addCallback(callback_func)
    return d

do()
reactor.run()

Sort of, but there is no concurrency in this type of event processing. No new callback will be called until the code gets back to the event loop. So the chain of callbacks is synchronous. It's only asynchronous in the event loop.

This is one caveat of this type of programming, the handlers most execute quickly, and get back to the event loop ASAP. It should not do any time consuming task in a handler.

Using a deferred does not make your code asynchronous.

import time
from twisted.internet import defer
from twisted.internet import reactor

def blocking(duration, deferred):
    print "start blocking"
    time.sleep(duration)
    print "finished blocking"
    deferred.callback(True)

def other_task():
    print "working..."
    reactor.callLater(1, other_task)

def finish(result):
    print "stopping reactor in 2sec"
    reactor.callLater(2, reactor.stop)

def failed(reason):
    print reason
    print "stopping reactor in 2sec"
    reactor.callLater(2, reactor.stop)

def main():
    d = defer.Deferred()
    d.addCallbacks(finish, failed)
    reactor.callLater(0, blocking, 5, d)

if __name__ == "__main__":
    reactor.callLater(0, other_task)
    main()
    reactor.run()

If you have long-running synchronous code, you can deferToThread or break it up into short iterations using a Cooperator (twisted.internet.task)

Unknown Soldier

If you want to make your code more asynchronous with a clean approach then check out this framework:

https://github.com/iogf/untwisted

It has neat code with clear documentation. The approach to dealing with asynchronous models is straightforward.

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