Promises!

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
 *
 * Utility for making XHR requests
 * returns a Promise and send response to resolve/reject
 *
 * @param  {String}   url
 */

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


/**
 * Promises
 */

// our first async req
get('https://api.myjson.com/bins/2qjdn')
.then(
  function(data){
    totalTweets.push(data.response);
    return  get('https://api.myjson.com/bins/3zjqz')
  })
.then(
  function(data){
    totalTweets.push(data.response);
    return  get('https://api.myjson.com/bins/29e3f')
  })
.then(
  function(data){
    totalTweets.push(data.response);
    showStats();
  },
  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;

  showTimeline();
}


/**
 * 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 tweetElement = el.cloneNode(true);
    tweetElement.classList.add('tweet');

    var name = el.cloneNode(true);
    name.classList.add('tweet__name');
    name.textContent = tweet.user.name;

    var handle = el.cloneNode(true);
   ...