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)