首先抛出我们在讨论使用回调编程时的一些观点:
激活errback是非常重要的。由于errback的功能与except块相同,因此用户需要确保它们的存在。他们并不是可选项,而是必选项。
不在错误的时间点激活回调与在正确的时间点激活回调同等重要。典型的用法是,callback与errback是互斥的即只能运行其中一个。
使用回调函数的代码重构起来有些困难。
Deferred
Twisted使用Deferred对象来管理回调函数的序列。有些情况下可能要把一系列的函数关联到Deferred对象上,以便在在异步操作完成时按次序地调用(这些一系列的回调函数叫做回调函数链);同时还要有一些函数在异步操作出现异常时来调用。当操作完成时,会先调用第一个回调函数,或者当错误发生时,会先调用第一个错误处理回调函数,然后Deferred对象会把每个回调函数或错误处理回调函数的返回值传递给链中的下一个函数。
Callbacks
一个twisted.internet.defer.Deferred对象代表着在将来某个时刻一定会产生结果的一个函数。我们可以把一个回调函数关联到Deferred对象上,一旦这个Deferred对象有了结果,这个回调函数就会被调用。另外,Deferred对象还允许开发者为它注册一个错误处理回调函数。Deferred机制对于各种各样的阻塞或者延时操作都为开发者提供了标准化的接口。
from twisted.internet import reactor, defer
def getDummyData(inputData): """ This function is a dummy which simulates a delayed result and returns a Deferred which will fire with that result. Don't try too hard to understand this. """ print('getDummyData called') deferred = defer.Deferred() # simulate a delayed result by asking the reactor to fire the # Deferred in 2 seconds time with the result inputData * 3 reactor.callLater(2, deferred.callback, inputData * 3) return deferred def cbPrintData(result): """ Data handling function to be added as a callback: handles the data by printing the result """ print('Result received: {}'.format(result)) deferred = getDummyData(3)deferred.addCallback(cbPrintData) # manually set up the end of the process by asking the reactor to# stop itself in 4 seconds timereactor.callLater(4, reactor.stop)# start up the Twisted reactor (event loop handler) manuallyprint('Starting the reactor')reactor.run()
多个回调函数
在一个Deferred对象上可以关联多个回调函数,这个回调函数链上的第一个回调函数会以Deferred对象的结果为参数来调用,而第二个回调函数以第一个函数的结果为参数来调用,依此类推。为什么需要这样的机制呢?考虑一下这样的情况,twisted.enterprise.adbapi返回一个Deferred对象——一个一个SQL查询的结果,可能有某个web窗口会在这个Deferred对象上添加一个回调函数,以把查询结果转换成HTML的格式,然后把Deferred对象继续向前传递,这时Twisted会调用这个回调函数并把结果返回给HTTP客户端。在出现错误或者异常的情况下,回调函数链不会被调用。
from twisted.internet import reactor, defer class Getter: def gotResults(self, x): """ The Deferred mechanism provides a mechanism to signal error conditions. In this case, odd numbers are bad. This function demonstrates a more complex way of starting the callback chain by checking for expected results and choosing whether to fire the callback or errback chain """ if self.d is None: print("Nowhere to put results") return d = self.d self.d = None if x % 2 == 0: d.callback(x * 3) else: d.errback(ValueError("You used an odd number!")) def _toHTML(self, r): """ This function converts r to HTML. It is added to the callback chain by getDummyData in order to demonstrate how a callback passes its own result to the next callback """ return "Result: %s" % r def getDummyData(self, x): """ The Deferred mechanism allows for chained callbacks. In this example, the output of gotResults is first passed through _toHTML on its way to printData. Again this function is a dummy, simulating a delayed result using callLater, rather than using a real asynchronous setup. """ self.d = defer.Deferred() # simulate a delayed result by asking the reactor to schedule # gotResults in 2 seconds time reactor.callLater(2, self.gotResults, x) self.d.addCallback(self._toHTML) return self.d def cbPrintData(result): print(result) def ebPrintError(failure): import sys sys.stderr.write(str(failure)) # this series of callbacks <b>本文来源gao@!dai!ma.com搞$$代^@码5网@</b>and errbacks will print an error messageg = Getter()d = g.getDummyData(3)d.addCallback(cbPrintData)d.addErrback(ebPrintError) # this series of callbacks and errbacks will print "Result: 12"g = Getter()d = g.getDummyData(4)d.addCallback(cbPrintData)d.addErrback(ebPrintError) reactor.callLater(4, reactor.stop)reactor.run()