Deferred object structure
Deferred consists of a series of pairs of callback chains, each pair containing a callback for handling success (callbacks) and a callback for handling errors (errbacks). Initially, deffereds will consist of two empty callback chains. Callbacks will always be added in pairs. When the result of the asynchronous processing is returned, the Deferred will start and trigger the callback chain in the order it was added.
It's probably easier to illustrate this with an example, so let's start with addCallback:
from import Deferred def myCallback(result): print result d = Deferred() (myCallback) ("Triggering callback.")
Running it will give you the following result:
Triggering callback.
The above example creates a deffered and uses its addCallback method to register a callback for handling success. The deffered is started and the callback chain is called. The arguments passed to the callback are also received by the first function in each callback chain.
There's addCallback, so the other error branch, I think I can guess that's addErrorback, again look at an example:
from import Deferred def myErrback(failure): print failure d = Deferred() (myErrback) (ValueError("Triggering errback."))
Running it will give you the following result:
[Failure instance: Traceback (failure with no frames): <type ''>: Triggering errback.]
You can see that Twisted will encapsulate the error in Failure.
It's worth noting that, as mentioned earlier, registered callbacks always come in pairs. When using the and methods, it may appear that we are just adding a callback or an errback, but in fact, to complete the creation of this level of the callback chain, these methods will also register a pass-through for the other half. keep in mind that the callback chain is always of the same length. To specify the callback and errback for this level of callbacks separately. you can use the methods:
d = Deferred() (myCallback, myErrback) ("Triggering callback.")
Advanced Examples
Next it's time for something a little more practical, and that's putting in a Reactor. let's look at an example first:
from import reactor, defer class HeadlineRetriever(object): def processHeadline(self, headline): if len(headline) > 50: (Exception("The headline ``%s'' is too long!" % (headline,))) else: (headline) def _toHTML(self, result): return "<h1>%s</h1>" % (result,) def getHeadline(self, input): = () (1, , input) (self._toHTML) return def printData(result): print result () def printError(failure): print failure () h = HeadlineRetriever() d = ("Breaking News: Twisted Takes us to the Moon!") (printData, printError) ()
The above example takes a caption and processes it, if the caption is too long it returns a super long error, otherwise it converts it to HTML and returns it.
Since the given title is less than 50 characters, the execution of the above code will return the following:
<h1>Breaking News: Twisted Takes us to the Moon!</h1>
One thing worth noting is that the above uses the callLater method of the reactor, which can be used for timed events to simulate an asynchronous request.
If we make the title long, for example:
h = HeadlineRetriever() d = ("1234567890"*6) (printData, printError)
That result can be met:
[Failure instance: Traceback (failure with no frames): <type ''>: The headline ``123456789012345678901234567890123456789012345678901234567890'' is too long!]
What's at stake in Deferreds
1. Deferreds will be triggered when their callback or errback is called;
2. Deferreds can only be triggered once! Attempts to trigger multiple times will result in an AlreadyCalledError exception;
3. Exceptions in the Nth level callback or errback will be passed to the N+1st level errback; if there is no errback, an Unhandled Error will be thrown; if no Exception is thrown or a Failure object is returned by the Nth level callback or errback, it will be handled by the callback in the N+1st level; if there is no Exception or Failure object, it will be handled by the callback in the N+1st level. If there is no errback, an Unhandled Error will be thrown;
4. The result returned in the callback will be passed to the next level of callback and used as its first parameter;
5. If the error passed to errback is not a Failure object, it will be automatically wrapped once.