A promise represents a future value,
such as the eventual result of an asynchronous operation.
Why should you use them?
the .then() method registers callbacks that handle the eventual value, or the failure to retrieve that value.
Promise { then: function, catch: function }
Promises are similar to event listeners, except:
The spec is still evolving, and there are several different proposals:
However Promises/A+ is now the most widely accepted.
32 + Chrome for Android |
30 25 as Promise (partial, behind flag*) 24 as Future |
Not yet supported | 19 | Not yet supported |
Polyfill: https://github.com/jakearchibald/es6-promise
var p = new Promise(function(resolve, reject) { resolve('success!'); }); p.then(function(val) { console.log(val); // logs "success!" });
Remember, the point of promises is to give us functional composition and error bubbling in the async world. They do this by saying that your functions should return a promise, which can do one of two things:
var existingUsers = ['arnold']; function createUser(user) { var userFound = existingUsers.indexOf(user) !== -1; return new Promise(function(resolve, reject) { if (userFound) { reject( user + ' is taken. Please choose another name.' ); } else { existingUsers.push(user); setTimeout(resolve, 1000, 'User ' + user + ' successfully created'); } }); }
createUser('hulk')
.then(function() {
return createUser('chuck');
}).then(function() {
return createUser('mr_t');
}).then(function(val) {
console.log('all done.', val)
}).catch(function(error) {
console.log(error);
});
/*
Logs: "all done. User mr_t successfully created"
Only the last resolved value is passed to .then()
*/
createUser('hulk')
.then(function() {
// arnold already exists,
// so this will return a rejected Promise
return createUser('arnold');
}).then(function() {
return createUser('chuck');
}).then(function() {
return createUser('mr_t');
}).then(function(val) {
console.log('all done.', val);
}).catch(function(error) {
console.log(error);
});
/*
Logs: "arnold is taken. Please choose another name."
Successfully creates 'hulk', but never reaches 'chuck' or 'mr_t'
*/
Promise.all(
[createUser('hulk'), createUser('chuck'), createUser('mr_t')]
).then(function(val) {
// returns an array of each resolved value
console.log(val);
}).catch(function(error) {
// returns the first error encountered
console.log(error);
});
/*
Logs: ["User hulk successfully created", "User chuck successfully created", "User mr_t successfully created"]
Waits for all of the Promises in the array to return a value before moving on
*/
Promise.race(
[createUser('hulk'), createUser('chuck'), createUser('mr_t')]
).then(function(val) {
console.log(val);
}).catch(function(error) {
console.log(error);
});
/*
Logs: "User hulk successfully created"
All of the Promises in the array get called, but only the first value returned gets passed to .then() or .catch()
*/
createUser('hulk')
.then(function(){
return createUser('arnold');
}).then(
function(){
return createUser('chuck');
},
function(err) {
// log this error and continue down the chain
console.log('Silent error:', err);
return 'fixed';
}
)
.then(function(){
return createUser('mr_t');
}).then(function(val){
console.log('all done.', val)
}).catch(function(error){
console.log(error);
});
/*
Logs:
"Silent error: arnold is taken. Please choose another name."
"all done. User mr_t successfully created"
user 'chuck' does not get created
.catch() is never called
*/
jQuery has a $.Deferred object, which is really just a handler for a Promise object.
It allows you to access some of the benefits of Promises and provides a variety of custom methods for handling resolved or rejected values.
However, it does not conform to the Promises/A+ spec and suffers from two major issues*:
*As of v2.1 beta
Methods
function createUser(user) { var userFound = existingUsers.indexOf(user) !== -1, $dfd = $.Deferred(); if (userFound) { $dfd.reject( user + ' is taken. Please choose another name.' ); } else { existingUsers.push(user); setTimeout($dfd.resolve, 1000, 'User ' + user + ' successfully created'); } return $dfd.promise(); }
createUser('hulk')
.then(function(){
return createUser('chuck');
}).then(function(){
return createUser('mr_t');
}).then(function(val){
console.log('all done.', val)
}).fail(function(error){
console.log(error);
});
/*
Logs: "all done. User mr_t successfully created"
jQuery uses .fail() instead of .catch()
*/
createUser('hulk')
.then(function(){
return createUser('arnold');
}).then(
function(){
return createUser('chuck');
},
function(err) {
// log this error and continue down the chain
console.log('Silent error:', err);
return 'fixed';
}
)
.then(function(){
return createUser('mr_t');
}).then(function(val){
console.log('all done.', val)
}).fail(function(error){
console.log('Error:', error);
});
/*
Logs:
"Silent error: arnold is taken. Please choose another name."
"Error: fixed"
The value 'fixed' is passed directly to .fail(), rather than the next .then(). This results not only in a confusing error message, but user 'mr_t' is also never created
*/
If your code throws an Error object anywhere, you're out of luck.
However it is possible to fix the rejection handler issue.
You can use another Promise library (such as Q) and wrap the function so that it returns a true promise:
var promise = Q.when($.get("https://github.com/kriskowal/q"));
Or, you can explicitly return a new $.Deferred() each time.
createUser('hulk')
.then(function(){
return createUser('arnold');
}).then(
function(){
return createUser('chuck');
},
function(err) {
var dfd = $.Deferred();
console.log('Silent error:', err);
dfd.resolve(err);
return dfd.promise();
}
)
.then(function(){
return createUser('mr_t');
}).then(function(val){
console.log('all done.', val)
}).fail(function(error){
console.log('Error:', error);
});
/*
Logs:
"Silent error: arnold is taken. Please choose another name."
"all done. User mr_t successfully created"
*/
Callbacks are inherently faster than Promises, however in most use cases the difference will be neglible.
If you need to perform hundreds of operations per second, then you may want to consider your options.
Promise library performance:
via: http://complexitymaze.com/2014/03/03/javascript-promises-a-comparison-of-libraries/
and jsPerf
Generators are functions which can be exited and later re-entered. Their context (variable bindings) will be saved across re-entrances.
yield suspends operation of the current function
function* addOne() { var i = 0; while(true) { yield i++; } }; var gen = addOne(); console.log(gen.next().value); // 0 console.log(gen.next().value); // 1 console.log(gen.next().value); // 2
"task.js is an experimental library for ES6 that makes sequential, blocking I/O simple and beautiful, using the power of JavaScript's new yield operator."
spawn(function*() { var data = yield $.ajax(url); $('#result').html(data); var status = $('#status').html('Download complete.'); yield status.fadeIn().promise(); yield sleep(2000); status.fadeOut(); });
task.js works with any framework or library that uses the Promises/A spec.