Skip to content

Instantly share code, notes, and snippets.

@kshirish
Last active August 29, 2015 14:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kshirish/bdfce1630fc308de5f6f to your computer and use it in GitHub Desktop.
Save kshirish/bdfce1630fc308de5f6f to your computer and use it in GitHub Desktop.
// 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