Callbacks: Nicer still

by Chris Buttery

HTML

<!-- status details -->
<div class="statList">
  <div class="statList__item">
    <div class="status">
      <div class="status__title">Tweets</div>
      <div class="status__count totalTweets"></div>
    </div>
  </div>
  <div class="statList__item">
    <div class="status">
      <div class="status__title">Photos</div>
      <div class="status__count totalPhotos"></div>
    </div>
  </div>
  <div class="statList__item">
    <div class="status">
      <div class="status__title">Favourites</div>
      <div class="status__count totalFavourites"></div>
    </div>
  </div>
</div>

<!-- tweet timeline -->
<div class="tweetFeed">
  <div class="timeLine"></div>
</div>

CSS

body {
  background: #f5f8fa;
  font: 16px/22px Arial, sans-serif;
  margin: 0;
}
.statList {
  background: #fff;
  border-bottom: 1px solid #e1e8ed;
  height: 60px;
  overflow: hidden;
}
.statList__item {
  float: left;
  width: 75px;
  height: 60px;
  display: table;
}
.status {
  display: table-cell;
  text-align: center;
  text-transform: uppercase;
  vertical-align: middle;
}
.status__title {
  color: #66757f;
  font-size: 11px;
  letter-spacing: .02em;
  text-transform: uppercase;
}
.status__count {
  color: #C29A34;
  font-size: 18px;
  font-weight: 500;
  padding-top: 3px;
}
.tweetFeed {
  margin: 10px;
}
.tweet {
  background: #fff;
  border: 1px solid #e1e8ed;
  padding: 15px;
}
.tweet:nth-child(1){
  border-radius: 5px 5px 0 0;
}
.tweet:nth-child(n+2){
  border-top: none;
}
.tweet__name{
  color: #292f33;
  display: inline-block;
  font-size: 14px;
  font-weight: bold;
}
.tweet__handle {
  color: #8899a6;
  display: inline-block;
  font-size: 13px;
  margin-left: 10px;
}
.tweet__message {
  margin-top: 5px;
}

JavaScript

var timeLine = document.querySelector('.timeLine');
var totalTweets = [];

/**
 * get
 *
 * Tiny utility for making XHR requests
 * @param  {String}   url
 * @param  {Function} callback [description]
 */

function get(url, callback, errback) {
  var req = new XMLHttpRequest();
  req.open('GET', url, true);
  req.onreadystatechange = function() {
    if(req.readyState != 4) return;
    if (req.status === 200) {
      callback(req);
    }
    else {
      errback(req);
    }
  };
  req.send(null);
}


/**
 * Asynch Requests
 */

// our first async req
function get1stTweet(){
  get('https://api.myjson.com/bins/2qjdn',
    function(data){
      totalTweets.push(data.response);
      next();
    },
    function(err){
      onError(err);
    }
  );
}

// our second async req wrapped in a 2 second delay
function get2ndTweet(){
  setTimeout(function(){
    get('https://api.myjson.com/bins/3zjqz',
      function(data){
        totalTweets.push(data.response);
        next();
      },
      function(err){
        onError(err);
      }
    );
  }, 2000);
}

// our third async req
function get3rdTweet(){
  get('https://api.myjson.com/bins/29e3f',
    function(data){
      totalTweets.push(data.response);
      next();
    },
    function(err){
      onError(err);
    }
  );
}


/**
 * showStats
 *
 * Populate some stats
 */

function showStats(){
  var totalTweets = document.querySelector('.totalTweets');
  totalTweets.textContent = countProperties('user') || 0;

  var totalPhotos  = document.querySelector('.totalPhotos');
  totalPhotos.textContent = countProperties('photo') || 0;

  var totalFavourites  = document.querySelector('.totalFavourites');
  totalFavourites.textContent = countProperties('favourited') || 0;

  next();
}


/**
 * showTimeline
 *
 * Flesh out some sweet sweet tweet data
 * then append to the .timeLine
 */

function showTimeline(){
  var el = document.createElement('div');

  totalTweets.forEach(function(tweet){
    tweet = JSON.parse(tweet);

    var...