<!DOCTYPE html>
<html>
<head>
<title>Async Fun: Six Demos</title>
<script data-require="jquery@2.1.1" data-semver="2.1.1" src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<h1>ASync Fun!</h1>
<section class="container">
<h2>Status:</h2>
<ul id="status"></ul>
</section>
<section class="container">
<h2>Responses:</h2>
<h3>Profile:</h3>
<pre id="profile"></pre>
<h3>Tweets:</h3>
<pre id="tweets"></pre>
<h3>Mentioned Friend:</h3>
<pre id="friend"></pre>
</section>
<script src="scripts.js"></script>
</body>
</html>
@import url('https://fonts.googleapis.com/css?family=Source+Sans+Pro');
body {
font-family: 'Source Sans Pro', sans-serif;
}
h1 {
text-align: center;
}
pre {
white-space: pre-line;
}
.container {
border: 3px solid #ccc;
padding: 20px;
margin: 10px 0;
}
.error {
color: red;
}
.success {
color: green;
}
# Async Fun
Describes 5 examples within `scripts.js` for processing data from an API, in order of increasing _awesomeness_, including:
1. Callbacks
2. Cleaner, named function-based callbacks
3. Promises
4. Fetch (a promise-based API that is part of ES6)
5. Generator Functions
Made for a Girl Develop It ES6 course.
{
"id": 10,
"username": "GDIBoston"
}
[
{
"id": 1,
"tweet": "Excited about tonight's @GDIBoston JavaScript class!",
"usersMentioned": [
{
"id": 10,
"username": "GDIBoston"
}
]
},
{
"id": 2,
"tweet": "Today's the second JavaScript class, yay!"
},
{
"id": 3,
"tweet": "So ready to code & make some websites!"
},
{
"id": 4,
"tweet": "Just graduated from my @GDIBoston JavaScript class :)",
"usersMentioned": [
{
"id": 10,
"username": "GDIBoston"
}
]
}
]
{
"id": 12302151,
"username": "coderlady5000",
"authLevel": "user",
"prefs": {
"homepageTweetsToShow": 2,
"showSidebar": false,
"fontSize": 12
}
}
/*
------------------------------
SHARED VARIABLES AND FUNCTIONS
------------------------------
*/
const statusContainer = document.getElementById('status');
const profileContainer = document.getElementById('profile');
const tweetsContainer = document.getElementById('tweets');
const mentionedFriendContainer = document.getElementById('friend');
// Promise-based request() function
const request = url => {
return new Promise((resolve, reject) => {
var xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.onload = function() {
if (this.status >= 200 && this.status < 300) {
resolve(JSON.parse(xhr.response));
} else {
reject({
status: this.status,
statusText: xhr.statusText
});
}
};
xhr.onerror = function() {
reject({
status: this.status,
statusText: xhr.statusText
});
};
xhr.send();
});
}
function handleError(err) {
if (err.status) {
statusContainer.innerHTML += `<li>Error ${err.status}: ${err.statusText}</li>`;
} else {
statusContainer.innerHTML += `<li>Error: ${err.toString()}</li>`;
}
statusContainer.classList.add('error');
}
//------------------------------------------------------------------------------
/*
--------------------
VERSION 1: CALLBACKS
--------------------
*/
// Get profile, then tweets, then mentioned friend
// STEP 1: Get profile
$.ajax({
type: 'GET',
url: 'json_profile.json', // Getting a JSON file acts just like hitting an API
success: profile => {
$(statusContainer).append('<li>Fetched profile</li>');
$(profileContainer).html(JSON.stringify(profile));
// STEP 2: Get tweets
$.ajax({
type: 'GET',
url: `json_tweets.json?id=${profile.id}`,
success: tweets => {
$(statusContainer).append('<li>Fetched tweets</li>');
$(tweetsContainer).html(JSON.stringify(tweets));
// STEP 3: Get mentioned friend
$.ajax({
type: 'GET',
url: `json_friend.json?id=${tweets[0].usersMentioned[0].id}`,
success: friend => {
$(statusContainer).append('<li>Fetched mentioned friend</li>');
$(mentionedFriendContainer).html(JSON.stringify(friend));
},
error: handleError
});
},
error: handleError
});
},
error: handleError
});
//------------------------------------------------------------------------------
/*
-----------------------------------------------
VERSION 2: CLEANER FUNCTION-REFENCING CALLBACKS
-----------------------------------------------
*/
// // STEP 1: Get profile
// $.ajax({
// type: 'GET',
// url: 'json_profile.json', // Getting a JSON file acts just like hitting an API
// success: getTweets, // STEP 2: Get tweets
// error: handleError
// });
// function getTweets(profile) {
// $(statusContainer).append('<li>Fetched profile</li>');
// $(profileContainer).html(JSON.stringify(profile));
// // Get tweets, passing our profile id
// $.ajax({
// type: 'GET',
// url: `json_tweets.json?id=${profile.id}`,
// success: getMentionedUser, // STEP 3: Get mentioned user
// error: handleError
// });
// }
// function getMentionedUser(tweets) {
// $(statusContainer).append('<li>Fetched tweets</li>');
// $(tweetsContainer).html(JSON.stringify(tweets));
// // Get friend mentioned in first tweet
// $.ajax({
// type: 'GET',
// url: `json_friend.json?id=${tweets[0].usersMentioned[0].id}`,
// success: friend => {
// $(statusContainer).append('<li>Fetched mentioned friend</li>');
// $(mentionedFriendContainer).html(JSON.stringify(friend));
// },
// error: handleError
// });
// }
//------------------------------------------------------------------------------
/*
-------------------------------
VERSION 3: PROMISES
-------------------------------
*/
// // STEP 1: Get profile
// request('json_profile.json')
// .then(profile => {
// statusContainer.innerHTML += '<li>Fetched profile</li>';
// profileContainer.innerHTML = JSON.stringify(profile);
// // STEP 2: Get tweets
// return request(`json_tweets.json?id=${profile.id}`);
// })
// .then(tweets => {
// statusContainer.innerHTML += '<li>Fetched tweets</li>';
// tweetsContainer.innerHTML = JSON.stringify(tweets);
// // STEP 3: Get mentioned friend
// return request(`json_friend.json?id=${tweets[0].usersMentioned[0].id}`);
// })
// .then(friend => {
// statusContainer.innerHTML += '<li>Fetched mentioned friend</li>';
// mentionedFriendContainer.innerHTML = JSON.stringify(friend);
// })
// .catch(err => handleError(err));
//------------------------------------------------------------------------------
/*
------------------------------------------------------------
VERSION 4: FETCH
Fetch is a promise-based API built into ES6 that we can use.
Much easier than writing our own request() function!
------------------------------------------------------------
*/
// // STEP 1: Get profile
// fetch('json_profile.json') // Getting a JSON file acts just like hitting an API
// // Return response JSON for processing in next then()
// .then(response => response.json())
// .then(profile => {
// statusContainer.innerHTML += '<li>Fetched profile</li>';
// profileContainer.innerHTML = JSON.stringify(profile);
// // Get tweets, passing our profile id
// return fetch(`json_tweets.json?id=${profile.id}`);
// })
// // STEP 2: Get tweets
// // Return response JSON for processing in next then()
// .then(tweetsResponse => tweetsResponse.json())
// .then(tweets => {
// statusContainer.innerHTML += '<li>Fetched tweets</li>';
// tweetsContainer.innerHTML += JSON.stringify(tweets);
// // Get friend mentioned in first tweet
// return fetch(`json_friend.json?id=${tweets[0].usersMentioned[0].id}`);
// })
// // STEP 3: Get mentioned friend
// // Return response JSON for processing in next then()
// .then(friendResponse => friendResponse.json())
// .then(friend => {
// statusContainer.innerHTML += '<li>Fetched mentioned friend</li>';
// mentionedFriendContainer.innerHTML += JSON.stringify(friend);
// })
// .catch(err => handleError(err));
//------------------------------------------------------------------------------
/*
------------------------------
VERSION 5: GENERATOR FUNCTIONS
------------------------------
*/
// function* createTweetFetcher() {
// // STEP 1: Get profile
// const profileID = yield request('json_profile.json');
// // STEP 2: Get tweets
// const friendID = yield request(`json_tweets.json?id=${profileID}`);
// // STEP 3: Get mentioned friend
// yield request(`json_friend.json?id=${friendID}`);
// }
// const tweetFetcher = createTweetFetcher();
// tweetFetcher.next().value
// .then(profile => {
// statusContainer.innerHTML += '<li>Fetched profile</li>';
// profileContainer.innerHTML = JSON.stringify(profile);
// return tweetFetcher.next(profile.id).value;
// })
// .then(tweets => {
// statusContainer.innerHTML += '<li>Fetched tweets</li>';
// tweetsContainer.innerHTML += JSON.stringify(tweets);
// const { id: friendID } = tweets[0].usersMentioned[0];
// return tweetFetcher.next(friendID).value;
// })
// .then(friend => {
// statusContainer.innerHTML += '<li>Fetched mentioned friend</li>';
// mentionedFriendContainer.innerHTML = JSON.stringify(friend);
// return tweetFetcher.next(); // final next() switches generator to done:true
// })
// .catch(err => handleError(err));
//------------------------------------------------------------------------------
/*
---------------------------------------------------------
VERSION 6: GENERATOR FUNCTIONS WITH RUNNER FUNCTION
- Source: https://davidwalsh.name/async-generators
---------------------------------------------------------
*/
// function* createTweetFetcher() {
// // STEP 1: Get profile
// const profile = yield request('json_profile.json');
// statusContainer.innerHTML += '<li>Fetched profile</li>';
// profileContainer.innerHTML += JSON.stringify(profile);
// // STEP 2: Get tweets
// const tweets = yield request(`json_tweets.json?id=${profile.id}`);
// statusContainer.innerHTML += '<li>Fetched tweets</li>';
// tweetsContainer.innerHTML += JSON.stringify(tweets);
// // STEP 3: Get mentioned friend
// const friend = yield request(`json_friend.json?id=${tweets[0].usersMentioned[0].id}`);
// statusContainer.innerHTML += '<li>Fetched mentioned friend</li>';
// mentionedFriendContainer.innerHTML = JSON.stringify(friend);
// }
// // A runner functions run a generator function to completion
// function runGenerator(generator) {
// const iterator = generator();
// // Asynchronously iterate over generator
// (function iterate(val){
// const yielded = iterator.next(val);
// if (!yielded.done) {
// // {done: false} -> since the yielded value is not 'done', continue
// if (typeof yielded.value.then === 'function') {
// // the yielded value is a promise
// yielded.value
// .then(iterate)
// .catch(err => handleError(err));
// } else {
// // Immediate value: just send right back in and
// // avoid synchronous recursion
// setTimeout(() => iterate(yielded.value), 0);
// }
// }
// })();
// }
// // Run the createTweetFetcher generator function to completion
// runGenerator(createTweetFetcher);