Handling collections of jQuery Deferreds with $.Promises.
What are $.Promises?
$.Promises object is a convenience wrapper around arrays of jQuery Deferreds or promises. It helps you to collect Deferreds and add new ones later on, to delay their resolution and pass them to
$.when even before all Deferreds of the collection are set up.
In short, a
$.Promises collection provides an an easy-to-read API for managing related Deferreds as a group, and for controlling their behaviour.
Dependencies and setup
jQuery.Promises is an extension for jQuery and requires jQuery 1.6.0 or newer. Include jquery.promises.js after jQuery is ready.
The stable version of jQuery.Promises is available in the
dist directory (dev, prod), including an AMD build (dev, prod). If you use Bower, fetch the files with
bower install jquery.promises. With npm, it is
npm install jquery.promises.
Making Promises is easy - postponing them, too
Here's how Promises work. We begin with a bit of bread-and-butter stuff:
var myPromises = new $.Promises();
new' is optional. We don't have to add Deferreds to the collection at this point, but we can:
var myPromises = $.Promises( dfd1, dfd2 );
This creates an aggregate promise which will resolve or fail according to the Deferreds 'inside' of it.
myPromises.done( ... ).fail( ... ); $.when( myPromises ).done( ... );
Now here's the thing. We can add more promises or Deferreds to the collection even if the current ones have all resolved. We just need to treat these Promises like new-year resolutions, and
// We delay the resolution of our promises and add some more myPromises.postpone() .add( dfd3, dfd4 ); // Now we attach a done handler $.when( myPromises ).done( whatever ); // ... and resolve all promises. dfd1.resolve( somearg ); dfd2.resolve(); // ... etc for all Deferreds
That would resolve the collection if we had not postponed it. But we have, so we can still add more stuff, and
$.when will wait for us to finish:
// $.when( myPromises ), called earlier, will respond to these additions myPromises.add( dfd5 ); ...
When all is set up,
$.when will act on the updated collection. This can't be done with ordinary arrays of promises. Finally,
will unblock the resolution or rejection of the aggregated promises.
$.when will now respond to their state.
$.Promises( [deferred, [deferred]] )
Constructor, returns a new Promises object. A list of promises can be passed as arguments (optional). Can be called with or without '
.add( promise, [promise] )
Adds one or more promises to the collection. Also accepts Deferreds. Returns the Promises object.
Blocks the resolution of the aggregate Promises. Returns the Promises object.
- if you pass Promises to
$.whenwhile you are still adding new promises to the collection, and want them to impact
- if you attach
.fail()handlers early, before you have made all your promises
- if you are in the process of gathering promises while the ones you have already added might resolve at any time. If all of them resolve, so does the collection - unless you have called
postpone()to keep the collection open for more promises.
- if you pass Promises to
Unblocks the resolution of the collected promises if it has been delayed by
postpone(). Returns the Promises object.
.ignoreBelated( [yesno] )
Makes the Promise object ignore attempts to add promises, or call
postpone(), when it is too late. Normally, these actions throw a
PromisesErrorexception if they happen after the eventual resolution or failure of the Promise.
Can be turned off again by calling
.ignoreBelated( false ). Returns the Promises object.
Returns if the Promises object is still unresolved.
$.Promises object is not built with maximum performance in mind. Look at the code - you won't see any of the 'low-level' stuff which makes up the jQuery implementation of Deferreds. Rather,
$.Promises is built on top of Deferreds. As a result, the code easy to read and maintain, but the implementation is not as efficient as it would otherwise be. That's the trade-off.
Build process and tests
If you'd like to fix, customize or otherwise improve the project: here are your tools.
npm sets up the environment for you.
- The only thing you've got to have on your machine (besides Git) is Node.js. Download the installer here.
- Clone the project and open a command prompt in the project directory.
- Run the setup with
npm run setup.
- Make sure the Grunt CLI is installed as a global Node module. If not, or if you are not sure, run
npm install -g grunt-clifrom the command prompt.
Your test and build environment is ready now. If you want to test against specific versions of jQuery, edit
Running tests, creating a new build
A handful of commands manage everything for you:
- Run the tests in a terminal with
- Run the tests in a browser interactively, live-reloading the page when the source or the tests change:
- Build the dist files (also running tests and linter) with
grunt build, or just
- Build continuously on every save with
- Change the version number throughout the project with
grunt setver --to=1.2.3. Or just increment the revision with
grunt setver --inc. (Remember to rebuild the project with
grunt getverwill quickly tell you which version you are at.
Finally, if need be, you can set up a quick demo page to play with the code. First, edit the files in the
demo directory. Then display
demo/index.html, live-reloading your changes to the code or the page, with
grunt demo. Libraries needed for the demo/playground should go into the Bower dev dependencies, in the project-wide
bower.json, or else be managed by the dedicated
bower.json in the demo directory.
grunt interactive and
grunt demo commands spin up a web server, opening up the whole project to access via http. By default, that access is restricted to localhost. You can relax the restriction in
Gruntfile.js, but be aware of the security implications.
Changing the tool chain configuration
In case anything about the test and build process needs to be changed, have a look at the following config files:
karma.conf.js(changes to dependencies, additional test frameworks)
Gruntfile.js(changes to the whole process)
web-mocha/_index.html(changes to dependencies, additional test frameworks)
New test files in the
spec directory are picked up automatically, no need to edit the configuration for that.
- Adapted for jQuery 3.2
- Adapted for jQuery 3.1
- Adapted for jQuery 3
- Fixed strict mode in AMD template
- Fixed and improved JSHint config
- Initial development, tests, documentation
Copyright (c) 2011-2017 Michael Heim.