Skip to content

Instantly share code, notes, and snippets.

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 carbide-public/f7f61a345d7e7880eea3b1326e3d4d3d to your computer and use it in GitHub Desktop.
Save carbide-public/f7f61a345d7e7880eea3b1326e3d4d3d to your computer and use it in GitHub Desktop.
ES2015 Promises and ES2017 AsyncAwait
import slowDiv from "./promises.js";
Promise.all([ ///All will return the results of all the promises (or reject at the first failure).
slowDiv(900, 3),
slowDiv(300, 100, 100),
slowDiv(5000, 10, 1000),
slowDiv(253, 11, 750)
]).then(results => console.log(results));
Promise.race([ ///Race will return the result of the first promise to resolve or reject.
slowDiv(900, 3),
slowDiv(300, 100, 100),
slowDiv(5000, 10, 1000),
slowDiv(253, 11, 750)
]).then(results => console.log(results));
import slowDiv from "./promises.js"; ///We're just reusing code from earlier...
async function asyncDiv(a, b, ms = 500) { ///Asynchronous functions are tagged with "async"
return await slowDiv(a, b, ms); ///We can await anything that returns a promise, including async functions, since they also return promises.
}
async function chainedDivision() {
const r1 = await asyncDiv(900, 3),
r2 = await asyncDiv(r1, 2),
r3 = await asyncDiv(r2, 5);
return r3; ///Each await above proceeds in serial, waiting until the previous promise is resolved.
}
async function altChainedDivision() {
return await asyncDiv(await asyncDiv(await asyncDiv(900, 3), 2), 5); ///We don't have to use temp variables if we don't want too... although this is harder to read, IMO.
}
async function go() {
const a = await chainedDivision(), ///We can only use "await" inside "async" functions.
b = await altChainedDivision();
console.log(a, b);
try { ///Inside "async" functions, if an await function throws an error, we can catch it using regular try...catch.
const c = await asyncDiv(5, 0);
} catch (err) {
console.error(err.message);
throw err;
}
}
go() ///We can call async functions outside side of an async function, but it will then be treated like a promise.
.catch(err => console.log(err.message)); ///... which means we can catch any thrown errors using the typical promise catch handling.
async function inSerial() { ///Be careful how you use it -- even code that looks like it might operate in parallel doesn't!
performance.now();
const arr = [await asyncDiv(900, 3), ///Each of these processes in serial, waiting for the previous item in the array to finish.
await asyncDiv(300, 100, 2000),
await asyncDiv(5000, 10, 1000),
await asyncDiv(253, 11, 5000)];
performance.now(); ///As you can see, ~8s have passed, not the ~5s that should have passed if these were operating in parallel.
return arr;
}
inSerial()
.then(results => console.log(results));
async function all(...promiseFns) { ///Here we show that async functions work well with promises -- we're explicitly returning a Promise that's waiting on all the promise factories passed in to finish.
return await Promise.all(promiseFns.map(promiseFn => promiseFn()));
}
all(async () => await asyncDiv(900, 3), ///Arrow functions can be async too -- just have "async" in front.
async () => await asyncDiv(300, 100, 100),
async () => await asyncDiv(5000, 10, 5000),
async () => await asyncDiv(253, 11, 750))
.then(results => console.log(results)); ///Because we're at the top level, we have to treat "all" like a promise, not like an async function.
function all2(...promiseFns) {
return Promise.all(promiseFns.map(promiseFn => promiseFn()));
}
// note, this works at the top level because carbide wraps us with a function, so it's not really the top level
const results = await all2(async () => await asyncDiv(900, 3),
async () => await asyncDiv(300, 100, 100),
async () => await asyncDiv(5000, 10, 5000),
async () => await asyncDiv(253, 11, 750));
results;
function slowDivCB(successCB, errorCB, a, b, ms = 500) { ///**ES5 Callbacks and Pyramid of Doom**
setTimeout(function() {
if (b === 0) errorCB(new Error("Can't divide by zero"));
else successCB(a / b);
}, ms);
}
slowDivCB(function (r) {console.log(r);}, function (err) {console.err(err);}, 10, 2);
slowDivCB(function (c) {
slowDivCB(function (d) {
slowDivCB(function (e) {
console.log(e); ///Imagine... it could be even worse than this!
}, function (err) {console.error(err);}, d, 2)
}, function (err) {console.error(err);}, c, 5)
}, function (err) {console.error(err);}, 900, 3);
function slowDiv(a, b, ms = 500) { ///**ES2015 and Promise Chaining**
return new Promise((resolve, reject) => {
setTimeout(() => {
if (b === 0) {
reject(new Error("Can't divide by zero"));
} else {
console.log(a / b);
resolve(a / b);
}
}, ms);
});
}
slowDiv(900, 3) ///Much better! It's obvious that each step proceeds in serial, and what the order of operations will be.
.then(c => slowDiv(c, 5))
.then(d => slowDiv(d, 2))
.then(e => console.log(e))
.catch(err => console.error(err));
export default slowDiv;
Promise.resolve(42) ///If we know the value, we can resolve it immediately
.then(n => console.log(n));
Promise.reject(new Error("Can't answer that question!")) ///The same applies to rejection
.then(n => console.log(n))
.catch(err => console.error(err.message));
function altSlowDiv(a, b, ms) { ///Here's an alternate way of writing slowDiv() using Promise.resolve instead of new Promise()
return Promise.resolve({
then: (resolve, reject) => {
setTimeout(() => {
if (b === 0) {
reject(new Error("Can't divide by zero"));
} else {
resolve(a / b);
}
}, ms);
}
});
}
altSlowDiv(700, 3)
.then(c => altSlowDiv(c, 5))
.then(d => altSlowDiv(d, 2))
.then(e => console.log(e))
.catch(err => console.error(err));
{ ///So why should we use Promise.resolve() even if we don't know the value immediately? Easy -- it's because of error handling. This couldThrow() function can throw an error BEFORE returning a promise.
const couldThrow = function(a, b) {
if (b === 0) throw new Error("b is zero!");
return new Promise(resolve => resolve(a / b));
}
try {
couldThrow(10, 0)
.then(r => console.log(r))
.catch(err => console.error(err.message));
} catch (err) {
console.error(`from outer catch: ${err.message}`); ///Which means that we have to have TWO error handling mechanisms. Also note that the catch in the promise chain NEVER gets called.
}
}
{
const couldThrow = function(a, b) {
return Promise.resolve() ///Now we return a promise, _no matter what._
.then(() => {
if (b === 0) throw new Error("b is zero!");
return a / b;
});
}
couldThrow(10, 0) ///Which means we know that any errors will be passed to our catch handler.
.then(r => console.log(r))
.catch(err => console.log(err.message));
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment