##The Good, The Bad, & The Ugly Ways of handling Async Operations With Javascript## #####Callbacks < Promises < Generators#####
###An Example: 5 in-sequence Async Operations### (also see parallel-sequence example: https://gist.github.com/willrstern/af3a3308fc5864cf48f8)
###The Ugly Way: Callbacks### After each function takes place, handle any errors & do the next thing - It's easy to walk through the code and understand what's going on...but it's ugly as sin - And, we have to handle errors all over the place ```javascript //Pyramid of DEATH!!! getUrl('/api/signin', function(errs) { if (errs) { //handle errors } else { getUrl('/api/profile', function(errs, profile) { if (errs) { //handle profile errors } else { getUrl('/api/accounts/'+profile.id, function(errs, accounts) { if (errs) { //handle accounts errors } else { getUrl('/api/messages/'+accounts[0].id, function(errs, messages) { if (errs) { //handle messages errors } else { getUrl('/api/message/'+messages[0].id, function(errs, message) { if (errs) { //handle first message errors } else { console.log('DONE!', message); } }); } }); } }); } }); } }); ``` ###The Bad Way: 'Clean' Callbacks### - We can clean it up by abstracting-out some of those functions - But it's still a TON of code - And we still catch errors left and right ```javascript getUrl('/api/signin', getProfile);
function getProfile (errs) { if (errs) { //handle signin errors } else { getUrl('/api/profile', getAccounts); } }
function getAccounts (errs, profile) { if (errs) { //handle profile errors } else { getUrl('/api/accounts/' + profile.id, getMessages); } }
function getMessages (errs, accounts) { if (errs) { //handle accounts errors } else { getUrl('/api/messages/' + accounts[0].id, getFirstMessage); } }
function getFirstMessage (errs, messages) { if (errs) { //handle accounts errors } else { getUrl('/api/message/' + messages[0].id, handleMessage); } }
function handleMessage (errs, message) { if (errs) { //handle accounts errors } else { console.log('DONE!', message); } }
###The Good Way: Promises###
Enter the **mostly** promised land.
- **Using promise libraries like Q, Bluebird, When, even jQuery, we can use the .then syntax**
- If a promise library is built right (i.e. not like jQuery), you can capture any errors once...at the end.
- It's also a TON less code, and way more readable code.
- If a .then() returns a promise object, it waits for it to fulfill before the next .then()
- If a .then() returns a normal value, the next .then() fires immediately - the first argument will be the return value of the previous .then()
```javascript
//WOW, now that's getting better
getUrl('/api/signin').then(function() {
return getUrl('/api/profile');
}).then(function(profile) {
return getUrl('/api/accounts/' + profile.id);
}).then(function(accounts) {
return getUrl('/api/messages/' + accounts[0].id);
}).then(function(messages) {
return getUrl('/api/message/' + messages[0].id);
}).then(function(message) {
console.log('DONE!', message);
}, function(errs) {
//handle errors on any of the events
});
###The Best Way: ES6 Generators...coming soon to an internet near you###
- Each time you run yield (passing it a promise or array of promises), the function pauses, waiting for the promise to complete, then resumes
- The value passed to var signin ISN'T A PROMISE, it's the resolved value of the promise
//with Bluebird library
Promise.coroutine(function* () {
var signin = yield getUrl('/api/signin');
var profile = yield getUrl('/api/profile');
var accounts = yield getUrl('/api/accounts/' + profile.id);
var messages = yield getUrl('/api/messages/' + accounts[0].id);
console.log('DONE!', yield getUrl('/api/message/' + messages[0].id));
})().catch(function(errs) {
//handle errors on any events
})
WHAT??? It's like magic! "Wrong...it IS magic"
//with Q library
Q.spawn(function* () {
try {
var signin = yield getUrl('/api/signin');
var profile = yield getUrl('/api/profile');
var accounts = yield getUrl('/api/accounts/' + profile.id);
var messages = yield getUrl('/api/messages/' + accounts[0].id);
console.log('DONE!', yield getUrl('/api/message/' + messages[0].id) );
} catch(errs) {
//handle any errors
}
});
//with Co library
co(function* () {
try {
var signin = yield getUrl('/api/signin');
var profile = yield getUrl('/api/profile');
var accounts = yield getUrl('/api/accounts/' + profile.id);
var messages = yield getUrl('/api/messages/' + accounts[0].id);
console.log('DONE!', yield getUrl('/api/message/' + messages[0].id) );
} catch(errs) {
//handle any errors
}
})();
//or handle errors with a callback
co(function* () {
var signin = yield getUrl('/api/signin');
var profile = yield getUrl('/api/profile');
var accounts = yield getUrl('/api/accounts/' + profile.id);
var messages = yield getUrl('/api/messages/' + accounts[0].id);
console.log('DONE!', yield getUrl('/api/message/' + messages[0].id) );
})(function(err) {
if (err) { console.log('err', err); }
});
And now you can enjoy your Javascript life as if for the first time. ####Now, let's checkout the parallel-sequence example: https://gist.github.com/willrstern/af3a3308fc5864cf48f8####
@WillStern There's a double
yield yield
in some of the examples. Is that a typo?