Skip to content

Instantly share code, notes, and snippets.

@willrstern
Last active February 13, 2018 17:29
Show Gist options
  • Save willrstern/a46567b4f489c4c30f29 to your computer and use it in GitHub Desktop.
Save willrstern/a46567b4f489c4c30f29 to your computer and use it in GitHub Desktop.
JAVASCRIPT AND ASYNC OPERATIONS

##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####

@ericclemmons
Copy link

@WillStern There's a double yield yield in some of the examples. Is that a typo?

@willrstern
Copy link
Author

Yep, that was copy/paste errors

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment