Skip to content

Latest commit

 

History

History
67 lines (51 loc) · 2.22 KB

service-async.md

File metadata and controls

67 lines (51 loc) · 2.22 KB

Asynchronous processing

The interceptor infrastructure supports asynchronous processing via core.async channels. Any interceptor that returns a channel will initiate asynchronous behavior. When a new context is later written to the channel, the interceptor path will continue processing with this new context in place of the original context.

This allows long-running work to occur without blocking a Web server thread.

Here is a synchronous handler that needs to wait for something to happen:

    (interceptors/defhandler takes-time [request]
      (ring-resp/response (lengthy-computation request)))

    (defroutes routes
      [[["/takes-time" {:get takes-time}]]])

Because this handler is synchronous, requests for /takes-time will block until the call to lengthy-computation completes, stopping the Web server thread from handling other work.

This code can be rewritten so that it releases the Web server thread, as shown below:

    (interceptors/defbefore takes-time
      [context]
      (let [channel (chan)]
        (go
         (let [result (lengthy-computation (:request context))
               new-context (assoc context :response (ring-resp/response result))]
           (>! channel new-context)))
        channel))

    (defroutes routes
      [[["/takes-time" {:get takes-time}]]])

The go block allows work to take place asynchronously while the thread is released. When that work is complete, the request will complete, and a response will be delivered to the client.

This facility allows a single value to be placed on a channel per interceptor; for more extensive use of channels for SSE, see Server-Sent Events.

For more about streaming, see Streaming Responses.