On Github geekingfrog / meetup-promise-talk
Wikipedia!
They describe an object that acts as a proxy for a result that is initially unknown, usually because the computation of its value is yet incomplete.
var promise = new Promise(function(
resolve,
reject
) {
// do something
});
var promise = new Promise(function(
resolve,
reject
) {
if(Math.random() > .5) resolve();
else reject();
}
function worldDomination() {
return hireHenchmen() // returns a promise
.then(trainHenchmen) // returns another promise
.then(makeHenchmemWork)
.then(profit);
} // returns the final result:
// profit after world domination
function giveMeFive() {
var two = Promise.resolve(2);
var four = two.then(function(result) {
return result + result;
})
return four.then(function(result) {
return 1 + result;
});
}
Promise.resolve(5)
.then(function(five) {
return five+1; // returns a value
})
.then(function(six) {
return Promise.resolve(six+1); // returns a promise
})
.then(function(seven) { /* ... */ });
doSomethingAsync(function(err, val) {
if(err) return console.error(err);
user.setVal(val);
});
-> % node boom.js
/home/greg/meetup/promises/boom.js:8
user.setVal(val);
^
ReferenceError: user is not defined
at /home/greg/meetup/promises/boom.js:8:3
at null._onTimeout (/home/greg/meetup/promises/boom.js:3:5)
at Timer.listOnTimeout (timers.js:133:15)
greg@archGreg [08:55:56] [~/meetup/promises]
-> % CRASH !
asyncStuff(function(err, val) {
try {
//...
} catch(error) {
// handle ...
}
})
function catchThemAll(fn) { // pokemon!
return function() {
try {
return fn.apply(this, arguments);
} catch(err) { /* handle */ }
}
}
asyncStuff(catchThemAll(callback));
process.on('uncaughtException', function(err) {
// handle
});
Blow the current stack.
But the rest of the program keeps running as if nothing happend.
Promise.resolve()
.then(function() { throw new Error('boom'); })
.then(function() { /* not executed */ })
.then(function() { /* not executed */ })
.catch(function(error) {
return 'there there'
})
.then(function(stuff) { stuff === 'there there'; });
And maybe even later if you don't feel it now
function randomService(id) {
return db.findById(id)
.then(checkObjectIsValid)
.then(getAnotherObject)
.then(checkRelations);
}
No more
if(error) return callback(error);
everywhere
Promise.resolve()
.then(function() { throw new Error('boom'); });
Promise.resolve()
.then(function() { throw new Error('boom'); })
.done();
/home/greg/meetup/promises/node_modules/bluebird/js/main/async.js:93
throw res.e;
^
Error: boom
at /home/greg/meetup/promises/boom.js:4:26
at tryCatch1 (/home/greg/meetup/promises/node_modules/bluebird/js/main/util.js:43:21)
at Promise$_callHandler [as _callHandler] (/home/greg/meetup/promises/node_modules/bluebird/js/main/promise.js:627:13)
[...]
//Don't do that
asyncStuff()
.then(function ok(data) {
// process data
}, function oops(error) {
// handle error
});
//This is better
asyncStuff()
.then(function ok(data) {
// process data
})
.catch(function oops(error) {
// handle error
});
Other libs
Using facebook API to post something on user's wall
FB.login(function(){
FB.api(
'/me/feed',
'post',
{message: 'Hello, world!'}
);
}, {scope: 'publish_actions'});
FB.login({scope: 'publish_actions'})
.then(function() {
FB.api(/* ... */);
})
var fbLogin = FB.login({scope: 'publish_actions'});
function postStuff(msg) {
return fbLogin.then(function() {
// post stuff on the user's wall
})
}
Promise.resolve(5)
.then(function(stuff) { console.log(stuff); });
console.log('done');
// done
// 5
$.get('/api/profile/me').then(function(profile) {
// do stuff
})
$.get('/api/user/abc')
{
"id": "abc",
"posts": ["123", "456", "789"]
}
Goal: get all the posts for the given user:
function getUserPosts(userId)
function getUserPosts(userId, callback) {
function getPost(postId, callback) {
$.get('/api/post/'+postId, callback);
}
$.get('/api/user/'+userId, function onSuccess(data) {
var asyncOperations = data.posts.map(function(postId) {
return function(cb) { getUserPosts(postId, cb); }
});
async.parallel(asyncOperations, function(error, posts) {
if(error) return callback(error);
else callback(posts);
})
}, callback);
}
function getUserPosts(userId) {
return Promise.cast($.get('/api/user/'+userId))
.then(function(user) {
return user.posts;
})
.map(function(postId) {
// you can return a promise here too!
return $.get('/api/post/'+postId);
}).all();
}
Promise.resolve([uri1, uri2, uri3])
.each(function(uri) {
return $.get(uri);
}).all();
Equivalent of async#serie
function getWithTimeout(uri, time) {
var jqXhr = $.get(uri);
return Promise.resolve(jqXhr) // same as Promise.cast
.timeout(time)
.catch(TimeoutError, CancellationError, function(e) {
jqXhr.abort();
throw e; // don't swallow the error
})
.catch(function(err) { // pokemon
console.error('Got error:', err);
});
}
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/test');
// pending connection to the db
var db = mongoose.connection;
db.on('error', console.error.bind(console, 'connection error:'));
db.once('open', function callback () {
// yay!
});
function connect() {
return new Promise(function(resolve, reject) {
var db = mongoose.connection;
db.once('error', reject);
db.once('open', resolve);
});
}
var db = connect();
function findAllUsers() {
return db.then(function() {
return User.findAll().exec();
})
}
Promise.resolve().then(function outer() {
return Promise.resolve().then(function inner() {
return Promise.resolve().then(function evenMoreInner() {
a.b.c.d()
}).catch(function catcher(e) {
console.error(e.stack);
});
});
});
ReferenceError: a is not defined
at evenMoreInner ([anonymous]:6:13)
at tryCatch1 ([anonymous]:41:19)
at Promise$_resolvePromise [as _resolvePromise] ([anonymous]:1739:13)
at Promise$_resolveLast [as _resolveLast] ([anonymous]:1520:14)
at Async$_consumeFunctionBuffer [as _consumeFunctionBuffer] ([anonymous]:560:33)
at Async$consumeFunctionBuffer ([anonymous]:515:14)
at MutationObserver.Promise$_Deferred ([anonymous]:433:17)
ReferenceError: a is not defined at evenMoreInner ([anonymous]:6:13) From previous event: at inner ([anonymous]:5:24) From previous event: at outer ([anonymous]:4:20) From previous event: at [anonymous]:3:9 at Object.InjectedScript._evaluateOn ([anonymous]:581:39) at Object.InjectedScript._evaluateAndWrap ([anonymous]:540:52) at Object.InjectedScript.evaluate ([anonymous]:459:21)
Be careful to performance cost
Promise.resolve().then(function() {
throw new Error('boom');
});
Possibly unhandled Error: boom
at /home/greg/meetup/promises/boom.js:4:26
at tryCatch1 (/home/greg/meetup/promises/node_modules/bluebird/js/main/util.js:43:21)
at Promise$_callHandler [as _callHandler] (/home/greg/meetup/promises/node_modules/bluebird/js/main/promise.js:627:13)
at Promise$_settlePromiseFromHandler [as _settlePromiseFromHandler] (/home/greg/meetup/promises/node_modules/bluebird/js/main/promise.js:641:18)
at Promise$_settlePromiseAt [as _settlePromiseAt] (/home/greg/meetup/promises/node_modules/bluebird/js/main/promise.js:800:14)
at Async$_consumeFunctionBuffer [as _consumeFunctionBuffer] (/home/greg/meetup/promises/node_modules/bluebird/js/main/async.js:75:12)
at Async$consumeFunctionBuffer (/home/greg/meetup/promises/node_modules/bluebird/js/main/async.js:38:14)
at process._tickCallback (node.js:339:11)
at Function.Module.runMain (module.js:492:11)
at startup (node.js:124:16)
$.get(
url,
function success(data) {
// handle success
}, function error(err) {
// handle error
}
);
$.get(url)
.then(function(data) {
//process
})
.fail(function(err) {
// handle
});
Promise.cast($.get(url)) .then(...) .catch(...);
Like indexedDB, websocket, most of the DOM api
var request = store.put({
"text": 'some text',
"timeStamp" : Date.now()
});
request.onsuccess = function(e) { /* do stuff */ };
request.onerror = function(e) { /* handle error */};
store.putAsync = function(data) {
return new Promise(function(resolve, reject) {
var request = store.put(data);
request.onsuccess = resolve;
request.onerror = reject;
});
}
If you're using bluebird Promise.promisifyAll
var fs = Promise.promisifyAll(require('fs'));
var file = fs.openAsync(path, 'r');
file.then(function(fd) { ... });
Promise.nodeify
function doAsyncStuff(callback) {
return asyncWithPromise()
.then(moreProcessing)
.nodeify(callback);
}
Promise.coroutine(function* () {
try {
let db = yield connect(); // async
let users = yield db.getUsers(); // async again
console.log('we got %d users here', users.length);
} catch(err) {
console.error('error:', err);
}
});
Async function can wait on promises
async function getUsers() {
try {
let db = await connect(); // async
let users = await db.getUsers(); // async again
console.log('we got %d users here', users.length);
} catch(err) {
console.error('error:', err);
}
}
Easy-to-use cross-platform design application
Meteor, Node.js, Node-webkit, SVG
Funded startup: GSFAccelerator (India)