Our Ajax Conventions–Clientside Continuations

As I mentioned in my previous post, I’m doing a write up of our AJAX conventions and how they’re paying off for us.

In this series of posts, I’m going to cover a few topics:

  1. The AjaxContinuation
  2. Clientside Continuations (this post)
  3. Request Correlation
  4. Validation


I have not had time to make this code public yet, but I promise I’ll get around to it. In the meantime, I’ll explain the concepts and just gist the highlights.

I’ve released an initial v0.1 version of this called jQuery.continuations.

The Setup

We start by hooking into jQuery’s global $.ajaxSetup call:

This is happening within a module block so the “self” is something I’ve declared above this piece of code.

Our convention is that unless you explicitly provide a success callback to any $.ajax call, you’re going to get this one for free.

Processing Continuations

I’ve talked about policies before and that’s exactly what we’re using to process the continuations. We have two functions: 1) matches(continuation) and 2) execute(continuation). Policies are registered via: $.continuations.applyPolicy() and we have several defaults.

Matches when refresh is true.
Simply refreshes the page.

Matches when url is not empty
Navigate the window to the specified url.

Matches when success is false and there are errors
Publishes the ‘ContinuationError’ topic through amplifyjs

Matches when the following properties exist: payload, topic
Publishes the specified topic and payload through amplifyjs

We have a couple more but I’ll save those for the next post.

Fun Fact

The payloadPolicy is an interesting one. We knew that we were going to be working w/ websockets but we didn’t start with them. Instead, we used this infrastructure and simply shoved topics/payloads into our continuations. When a response was received, the information was published through amplify and we had structured our code to work against those topics.

When it came time to turn on websockets, we simply started publishing websocket messages through amplify and everything kept working like a charm.

Until next time.

About Josh Arnold

Josh is team lead at Extend Health and a principal developer on the Fubu-family of frameworks. He is a proud husband, terrified but excited father, passionate software guy and coach, closet musician, aspiring man of God, and a perpetual learner.
This entry was posted in General and tagged , , , . Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • Ryan

    This is very cool! One question though. Whenever you request data from the server do you have to send a “topic” with the payload, or do you use the correlationId somehow? It seems that requiring a topic server-side could lead to some mixing of UI and non-UI concerns. As far as I could tell, correlationId is only available on the “AjaxCompleted” event.  I suppose if you are streaming multiple results with web sockets the topic concept makes more sense.

    • Anonymous

      Let’s split those two up for clarity: the server simply tracks the correlation id so you have more control of your requests. Honestly, this is most beneficial in one place: automated testing. We keep track of pending requests so that we have a fairly reliable condition to test against while we “wait” for async stuff to process.

      The topic/payload piece is just part of the pipeline and are completely optional. We’ve found that the continuations represent more of a snapshot of the system state and then we layer on metadata. That’s vague so let me expand on that with an example:

      Our system is time-sensitive and we capture “timestamps” for entities in the system (e.g., “The patient was here at 12:00″). Our first pass at the endpoint for this sent back a continuation with the topic/payload of: “timestamp-updated”/{ relevant entity info }. That was then relayed through our clientside event aggregator. When websockets were introduced, the clientside didn’t have to change because it was already listening for that topic. My point being: the topic/payload thing works great for event-based systems (i.e., event sourcing).
      I understand your hesitation about the mixing of UI/non-UI concerns but consider it from more of an API approach where you provide entity-related information. An example being: Create a User. The response could return the id of the User and some urls (github does this in their api when requesting resources that are paginated).

      • Ryan

        Excellent. I just realized I could mix event streaming and still use a traditional $.ajax() success callback to have it basically skip this whole pipeline when events are not needed. I was thinking you routed all responses through Amplify.

        • Anonymous

          Yes, precisely. We actually plugin to $.ajax as a global convention. Basically if you call: $.ajax and do not specify a success callback, then this fires: 

          If you want to participate in that pipeline, you can call $.continuations.applyPolicy. (I cover this in my latest post http://lostechies.com/josharnold/2012/02/18/jquery-continuations-in-action/)

          You can also specify a continuationSuccess callback to $.ajax that lets you hook in before the pipeline. Or just provide a success callback and override altogether.