我们已经尝试玩了下Deferreds(什么?不知情?快去看看《twisted异步之路:Deferred(一)》)。接下来就应该来点更为实际的,那就是放进Reactor。先来看一个例子:
from twisted.internet import reactor, defer class HeadlineRetriever(object): def processHeadline(self, headline): if len(headline) > 50: self.d.errback(Exception("The headline ``%s'' is too long!" % (headline,))) else: self.d.callback(headline) def _toHTML(self, result): return "<h1>%s</h1>" % (result,) def getHeadline(self, input): self.d = defer.Deferred() reactor.callLater(1, self.processHeadline, input) self.d.addCallback(self._toHTML) return self.d def printData(result): print result reactor.stop() def printError(failure): print failure reactor.stop() h = HeadlineRetriever() d = h.getHeadline("Breaking News: Twisted Takes us to the Moon!") d.addCallbacks(printData, printError) reactor.run()
上例接收一个标题并对其进行处理,如果标题超长会返回超长的错误,否则将其转为HTML并返回。
因所给的标题少于50个字符,故执行以上代码会得到如下返回:
<h1>Breaking News: Twisted Takes us to the Moon!</h1>
有一点值得注意的,上面用到了reactor的callLater方法,它可以用来做定时事件从而模拟一个异步的请求。
如果我们将标题变得很长,比如说:
h = HeadlineRetriever() d = h.getHeadline("1234567890"*6) d.addCallbacks(printData, printError)
那结果是可以遇见的:
[Failure instance: Traceback (failure with no frames): <type 'exceptions.Exception'>: The headline ``123456789012345678901234567890123456789012345678901234567890'' is too long!]
我们用图看一下触发流程:
Deferreds中的关键之处
1. Deferreds将会在调用其callback或errback时被触发;
2. Deferreds仅能被触发一次!如果尝试多次触发将会导致AlreadyCalledError异常;
3. 第N级callback或errback中的Exceptions将会传入第N+1级的errback中;如果没有errback,则会抛出Unhandled Error。如果第N级callback或errback中没有抛出Exception或返回Failure对象,那接下来将会由第N+1级中的callback进行处理;
4. callback中返回的结果将会传入下一级callback,并作为其第一个参数;
5. 如果传入errback的错误不是一个Failure对象,那将会被自动包装一次。