c) and fun. Today, I’ll show you how to adapt a third party library that uses callbacks to a promise interface.
We’ll be using jQuery.Deferred
There are many great promise libraries:
- Q — A tool for making and composing asynchronous promises
- RSVP.js — tools for organizing asynchronous code
- when.js — the promise library from cujoJS
There’s even a spec for promises in ES6.
However, until the native spec is fully supported we’ll be using jQuery.Deferred  on the client.
A Typical Scenario
We recently built a new internal app to handle some administrative tasks. We used Bootstrap to get up and running quickly, and we stumbled upon a great library for handling our dialog boxes called bootbox.js from Nick Payne.
Here is a typical snippet of code, straight from bootbox:
With this code, bootbox will open a dialog containing the
"Hello world!" text. The callback is fired after the user clicks on the
"ok" button and the
Example.show("Hello world callback"); code is executed.
This is already sweet, but soon we found ourselves writing a lot of boilerplate when we implemented the
That’s not overly messy, but handling these conditions in every confirmation dialog with inline callbacks didn’t appeal to us.
Wrapping Bootbox With an Adapter
First we created a new object that wraps the bootbox logic. We called it Popper (we’re not always clever with lib naming).
Returning a Deferred Object
The above snippet isn’t very impressive. However, now that we have a wrapper we can adapt the bootbox interface to use a deferred object:
In effect, confirming the bootbox dialog will resolve the deferred object, and canceling the dialog will reject the deferred object. Our implementation code now looks like this:
This has a couple of advantages:
* There are no more messy
if blocks to parse; the code is more “flat”.
* We’ve changed the flow of the application from using a single, multipurpose callback into using separate callbacks with unique concerns.
jQuery.ajax returns a Deferred
There were many cases where we would fire off an ajax request after the user confirmed the dialog. Since jQuery returns a deferred object from
$.ajax, we were able to envelop that logic into our adapter as well:
A Few Steps Further
The above code is nice, but there are still a few outstanding issues:
.fail are not very semantic method names for confirming or canceling a dialog.
* It would be nice to register callbacks that fire as soon as the buttons are clicked, and additional callbacks that fire after the ajax responds.
To solve these issues, we created a map of semantic method names (
ok maps to
cancel maps to
fail, etc). Popper’s methods return a new object that contains these semantic methods, making our implementation code look beautiful:
Here’s a link to the full Popper.js source.
I hope I’ve expanded your toolbelt a little by showing you how to use a promise pattern in a real-world scenario.
Do you like the Popper lib? Leave us a comment!
[ 1 ] We know that jQuery.Deferred objects are not Promise/A+ compliant, and we sympathize with why that’s non-optimal. However, since we are already using jQuery in most of our projects, jQuery.Deferred solves 95% of our needs without requiring us to depend on an additional library.