Twisted Documentation: Asynchronous Responses (via Deferred)

# Asynchronous Responses (via Deferred)

The previous example had a `Resource` that generates its response asynchronously rather than immediately upon the call to its render method. Though it was a useful demonstration of the `NOT_DONE_YET` feature of Twisted Web, the example didn't reflect what a realistic application might want to do. This example introduces `Deferred`, the Twisted class which is used to provide a uniform interface to many asynchronous events, and shows you an example of using a `Deferred`-returning API to generate an asynchronous response to a request in Twisted Web.

`Deferred` is the result of two consequences of the asynchronous programming approach. First, asynchronous code is frequently (if not always) concerned with some data (in Python, an object) which is not yet available but which probably will be soon. Asynchronous code needs a way to define what will be done to the object once it does exist. It also needs a way to define how to handle errors in the creation or acquisition of that object. These two needs are satisfied by the callbacks and errbacks of a `Deferred`. Callbacks are added to a `Deferred` with `Deferred.addCallback`; errbacks are added with `Deferred.addErrback`. When the object finally does exist, it is passed to `Deferred.callback` which passes it on to the callback added with `addCallback`. Similarly, if an error occurs, `Deferred.errback` is called and the error is passed along to the errback added with `addErrback`. Second, the events that make asynchronous code actually work often take many different, incompatible forms. `Deferred` acts as the uniform interface which lets different parts of an asynchronous application interact and isolates them from implementation details they shouldn't be concerned with.

That's almost all there is to `Deferred`. To solidify your new understanding, now consider this rewritten version of `DelayedResource` which uses a `Deferred`-based delay API. It does exactly the same thing as the previous example. Only the implementation is different.

First, the example must import that new API that was just mentioned, `deferLater`:

    1
    from twisted.internet.task import deferLater

Next, all the other imports (these are the same as last time):

    1
    2
    3
    from twisted.web.resource import Resource
    from twisted.web.server import NOT_DONE_YET
    from twisted.internet import reactor

With the imports done, here's the first part of the `DelayedResource` implementation. Again, this part of the code is identical to the previous version:

    1
    2
    3
    4
    class DelayedResource(Resource):
        def _delayedRender(self, request):
            request.write("<html><body>Sorry to keep you waiting.</body></html>")
            request.finish()

Next we need to define the render method. Here's where things change a bit. Instead of using `callLater`, We're going to use `deferLater` this time. `deferLater` accepts a reactor, delay (in seconds, as with `callLater`), and a function to call after the delay to produce that elusive object discussed in the description of `Deferred`s. We're also going to use `_delayedRender` as the callback to add to the `Deferred` returned by `deferLater`. Since it expects the request object as an argument, we're going to set up the `deferLater` call to return a `Deferred` which has the request object as its result.

    1
    2
    3
    ...
        def render_GET(self, request):
            d = deferLater(reactor, 5, lambda: request)

The `Deferred` referenced by `d` now needs to have the `_delayedRender` callback added to it. Once this is done, `_delayedRender` will be called with the result of `d` (which will be `request`, of course — the result of `(lambda: request)()`).

    1
    2
    ...
            d.addCallback(self._delayedRender)

Finally, the render method still needs to return `NOT_DONE_YET`, for exactly the same reasons as it did in the previous version of the example.

    1
    2
    ...
            return NOT_DONE_YET

And with that, `DelayedResource` is now implemented based on a `Deferred`. The example still isn't very realistic, but remember that since `Deferred`s offer a uniform interface to many different asynchronous event sources, this code now resembles a real application even more closely; you could easily replace `deferLater` with another `Deferred`-returning API and suddenly you might have a resource that does something useful.

Finally, here's the complete, uninterrupted example source, as an rpy script:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    from twisted.internet.task import deferLater
    from twisted.web.resource import Resource
    from twisted.web.server import NOT_DONE_YET
    from twisted.internet import reactor

    class DelayedResource(Resource):
        def _delayedRender(self, request):
            request.write("<html><body>Sorry to keep you waiting.</body></html>")
            request.finish()

        def render_GET(self, request):
            d = deferLater(reactor, 5, lambda: request)
            d.addCallback(self._delayedRender)
            return NOT_DONE_YET

    resource = DelayedResource()

Index

Version: 13.2.0