Last active
August 29, 2015 14:18
-
-
Save kshirish/bdfce1630fc308de5f6f to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Reference: http://www.html5rocks.com/en/tutorials/es6/promises/ | |
// `Promises` have been introduced natively as a part of ES6 now. In this blogpost hopefully I will | |
// try to explore all the aspects of `Promises`. | |
// So what is a Promise? | |
// `Promises` are merely a prettier way of writing code which makes an asynchronous code look like synchronous. | |
// Usual way of XHR | |
$.get("script.php", function(data) { | |
console.log('Your account has been created successfully!'); | |
}); | |
// With jQuery Promises | |
var request = $.ajax({ | |
url: "script.php", | |
method: "POST", | |
data: { | |
'user': formData.username, | |
'pwd': formData.password | |
} | |
}); | |
request.done(function(msg) { | |
console.log('Your account has been created successfully!'); | |
}); | |
request.fail(function(jqXHR, textStatus) { | |
console.log('Looks like the account already exists'); | |
}); | |
// `Promises` are very much similar to event handling but there are some minor differences. | |
// A promise can only succeed or fail once. | |
// If a promise has succeeded or failed and you later add a success/failure callback, | |
// the correct callback will be called, even though the event took place earlier | |
// Promises have been around for a while in the form of libraries, | |
// like `Q` and `when` which have a standardised behaviour called Promises/A+ same as `ES6 Promises`. | |
// jQuery also has a Promise type called deferreds but they are not Promises/A+ compliant. | |
var promise = new Promise(function(resolve, reject) { | |
// do your async thing | |
var req = new XMLHttpRequest(); | |
req.open('GET', url); | |
req.onload = function() { | |
if (req.status == 200) { | |
resolve(req.response); // resolve here | |
} else { | |
reject(Error(req.statusText)); // reject here | |
} | |
}; | |
req.onerror = function() { | |
reject(Error("Network Error")); // reject here | |
}; | |
req.send(); | |
}); | |
promise.then(function(result) { // if Resolved | |
console.log(result); // Response | |
}, function(err) { // if Rejected | |
console.log(err); // Error | |
}); | |
// Is that it??? | |
// Chaining - when you have mutiple Ajax to do. | |
var promise = new Promise(function() { | |
if ( /* condition */ ) { | |
resolve(1); | |
} else { | |
reject(-1); | |
} | |
}); | |
promise.then(function(data) { | |
data++; | |
return data; | |
}, function(err) { | |
err--; | |
return err; | |
}).then(function(data) { | |
data++; | |
return data; | |
}, function(err) { | |
err--; | |
return err; | |
}) | |
// Use chaining as middlewares | |
get('story.json').then(function(response) { | |
return JSON.parse(response); | |
}).then(function(response) { | |
console.log("Yey JSON!", response); | |
}); | |
// `then` is smarter than this so if you return a promise | |
// the next `then` waits for that `promise` to get resolved | |
getJSON('story.json').then(function(story) { | |
return getJSON(story.chapterUrls[0]); | |
}).then(function(chapter1) { | |
console.log("Got chapter 1!", chapter1); | |
}); | |
// case 1: | |
get('story.json').then(function(response) { | |
console.log("Success!", response); | |
}, function(error) { | |
console.log("Failed!", error); | |
}); | |
// case 2: Suger for reject: `catch` | |
get('story.json').then(function(response) { | |
console.log("Success!", response); | |
}).catch(function(error) { | |
console.log("Failed!", error); | |
}); | |
// case 3: | |
get('story.json').then(function(response) { | |
console.log("Success!", response); | |
}).then(undefined, function(error) { | |
console.log("Failed!", error); | |
}); | |
// Notice above three cases, only 2nd and 3rd are equivalent. | |
// If there is an error in `resolve` callback it will go to the next | |
// `then(undefined, function() {})` and `catch` | |
// which is not the case in 1st. | |
// In case 1: only will execute no matter what! | |
// Check out below example, you will notice one interesting thing | |
// if for some reason asyncThing1, asyncThing2 or asyncThing3 fails | |
// then it will go to the immediate `catch` block | |
asyncThing1().then(function() { | |
return asyncThing2(); | |
}).then(function() { | |
return asyncThing3(); | |
}).catch(function(err) { | |
return asyncRecovery1(); | |
}).then(function() { | |
return asyncThing4(); | |
}, function(err) { | |
return asyncRecovery2(); | |
}).catch(function(err) { | |
console.log("Don't worry about it"); | |
}).then(function() { | |
console.log("All done!"); | |
}); | |
// Implicit error | |
var promise = new Promise(function(resolve, reject) { | |
resolve(JSON.parse("This ain't JSON")); | |
}); | |
promise.then(function(data) { | |
console.log("It worked!", data); // expected to come here but | |
}).catch(function(err) { | |
console.log("It failed!", err); // comes here | |
}); | |
// I think I understood everything, now I can do things on my own. | |
story.chapterUrls.forEach(function(chapterUrl) { | |
// Fetch chapter | |
getJSON(chapterUrl).then(function(chapter) { | |
// and add it to the page | |
addHtmlToPage(chapter.html); | |
}); | |
}); | |
// Not so quick! | |
// You got to learn the art of `sequence`. | |
// Start off with a promise that always resolves | |
var sequence = Promise.resolve(); | |
// Loop through our chapter urls | |
story.chapterUrls.forEach(function(chapterUrl) { | |
// Add these actions to the end of the sequence | |
sequence = sequence.then(function() { | |
return getJSON(chapterUrl); | |
}).then(function(chapter) { | |
addHtmlToPage(chapter.html); | |
}); | |
}); | |
// Do you smell something wrong with the above approach ? | |
// What we are doing above? - We are making one AJAX call when the response of | |
// previous one is received but we can do much better than that! | |
// Browsers are pretty good at downloading multiple things at once, | |
// so we're losing performance by downloading chapters one after the other. | |
// What we want to do is download them all at the same time, | |
// then process them when they've all arrived. | |
var arrayOfPromises = [ | |
getPromise('http://api.openweathermap.org/data/2.5/weather?lat=35&lon=139'), | |
getPromise('http://api.icndb.com/jokes/random'), | |
getPromise('http://api.openweathermap.org/data/2.5/weather?lat=135&lon=19'), | |
getPromise('http://api.icndb.com/jokes/random'), | |
getPromise('http://api.openweathermap.org/data/2.5/weather?lat=15&lon=29'), | |
getPromise('http://api.icndb.com/jokes/random'), | |
getPromise('http://api.openweathermap.org/data/2.5/weather?lat=13&lon=90'), | |
getPromise('http://api.icndb.com/jokes/random'), | |
getPromise('http://api.icndb.com/jokes/random') | |
]; | |
Promise.all(arrayOfPromises).then(function(arrayOfResults) { | |
console.log('All Promises got resolved!'); // All Promises got resolved in same order they were called! | |
console.log(arrayOfResults); | |
}).catch(function(err) { | |
console.log('Atleast one of them died on the way!'); // Atleast one of them died on the way! | |
}); | |
// Description : Whatever resolves/rejects first will be returned | |
var p1 = new Promise(function(resolve, reject) { | |
setTimeout(resolve, 500, "one"); | |
}); | |
var p2 = new Promise(function(resolve, reject) { | |
setTimeout(resolve, 100, "two"); | |
}); | |
Promise.race([p1, p2]).then(function(value) { | |
console.log(value); | |
}); // "two" | |
var p3 = new Promise(function(resolve, reject) { | |
setTimeout(resolve, 100, "three"); | |
}); | |
var p4 = new Promise(function(resolve, reject) { | |
setTimeout(reject, 500, "four"); | |
}); | |
Promise.race([p3, p4]).then(function(value) { | |
console.log(value); // "three" | |
}, function(reason) { | |
// Not called | |
}); | |
var p5 = new Promise(function(resolve, reject) { | |
setTimeout(resolve, 500, "five"); | |
}); | |
var p6 = new Promise(function(resolve, reject) { | |
setTimeout(reject, 100, "six"); | |
}); | |
Promise.race([p5, p6]).then(function(value) { | |
// Not called | |
}, function(reason) { | |
console.log(reason); // "six" | |
}); | |
// Recipe of the day: Generators and Promises | |
// Now comes the `Generators`. | |
// It was not covered in detail in my other post about ES6. | |
// Let's see what does it bring to the table. | |
var path = 'http://www.html5rocks.com/en/tutorials/es6/promises/'; | |
var asyncFun = function(someUrl, resolve, reject) { | |
$.get(someUrl) | |
.done(function(response) { | |
if (response) { | |
resolve(response); | |
} else { | |
reject('Empty Response'); | |
} | |
}) | |
.error(function(response) { | |
reject('Empty Response'); | |
}); | |
}; | |
var getPromise = function(someUrl) { | |
return new Promise(function(resolve, reject) { | |
asyncFun(someUrl, resolve, reject); // do some async | |
}); | |
}; | |
var addHtmlToPage = function(html) { | |
console.log('Below HTML added to page!'); | |
console.log(html); | |
}; | |
function* generatorFun() { | |
var path = 'http://www.html5rocks.com/en/tutorials/es6/promises/'; | |
var stories = yield getPromise(path + 'story.json'); | |
stories.chapterUrls.forEach(function(chapterUrl) { | |
yield getPromise(path + chapterUrl); | |
}); | |
} | |
function runGenerator() { | |
var it = generatorFun(); | |
it.next().then(function(stories) { | |
console.log(stories); | |
return it.next(stories); | |
}).then(function(chapter) { | |
console.log(chapter); | |
return it.next(); | |
}).then(function(chapter) { | |
console.log(chapter); | |
return it.next(); | |
}).then(function(chapter) { | |
console.log(chapter); | |
return it.next(); | |
}).then(function(chapter) { | |
console.log(chapter); | |
return it.next(); | |
}).then(function(chapter) { | |
console.log(chapter); | |
}); | |
} | |
runGenerator(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment