jQuery deferred and pipe

Recently I needed to do some client side validation that had many steps. Each of those steps had processing, then ultimately an ajax call. Step two needed to be done after step one but only if step one was successful. I had about four steps to do in total. Normally I would just chain these ajax calls on one another but these were in modules and I didn’t really want to be doing $.ajax(url).success(function () { step2(); }) because a part of me was just not ok with step1 knowing about step2. To complete the validation I wrote some pretty complicated callback logic and actually got this to work but I knew there had to be a better way.

Last weekend I spoke at Pittsburgh Tech Fest. It really was a nice event. It was good to see non dot net stuff at a conference for once and it was also interesting to see that the majority of the sessions were on javascript or jQuery. I attended several of the javascript/jQuery sessions and it seemed like they were so close to answering my questions about chaining validation together. I heard things about jQuery deferred, promises, piping and when. All these things were generally brushed over so I decided to research them more after the conference. I later found out that piping is what I wanted. Pipe is done off of a jQuery deferred object. You can create your own deferred objects but that looked kind of confusing and I was in a hurry. I found that calls to ajax return an object that implements the promise interface though. Every example I could see of piping or of deferred was doing something basic like a few ajax calls wrapped in a when. I set out to create an example that was slightly more complicated so that I could truly understand how deferred and pipe worked.

I’ve been using the javascript revealing module pattern for quite some time now, so most of my processing is done in some method inside of one of these modules. Don't be scared by it. I'll just show you the jsfiddle I made. It's pretty simple. Then I'll explain.

Link to jsFiddle

You'll notice two modules in there to demonstrate two steps that rely on each other's completion but they don't know about each other. Then toward the bottom I have:

    .done(function () { log("the process completed successfully"); })
    .fail(function () { log("one of the steps failed"); })
    .always(function () { log("end of process"); });

This is where we can have one place to have it pretty readable that step1 executes then step2. Since pipe returns a deferred object I can chain as many as I want together but then assign callbacks for the entire process.

  • Done will execute when both step1 and step2 have completed.
  • Fail will execute if step1 or step2 fails.
  • Always will execute regardless of done or fail.

So you ultimately get the output of:

start of process

starting step 1

step1 done

starting step 2

step2 done

the process completed successfully

end of process

Pretty cool, right?

I also added some flags at the top if you wanted to play around with what happens if one or both of the steps fail. If I tell step1 to fail, this is the output:

start of process

starting step 1

step1 failed

one of the steps failed

end of process

I also added some commented out code at the bottom if you ever had a need to run two processes at the same time (not dependent on each other). The when statement will do that for you and it will return a deferred object so that you can use those general done, fail, always callbacks. Go ahead and try it out.

This exercise really helped me understand jQuery deferred objects and the pipe method. I hope it does the same for you.