Asynchronous Javascript
by Daniel Dyla
I’m thinking about adding a new post here about the evolution of asynchronous javascript. Until that comes, maybe this will help.
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
/* | |
* First, we will declare some asynchronous functions to consume. | |
* These actually do not make asynchronous calls, but they illustrate | |
* the idea. | |
*/ | |
// Callback style asynchronous function | |
function callbackAsyncFunction(val1, val2, callback) { | |
//processing | |
if (!val1) { | |
callback(new Error("val1 required")); | |
} else if (!val2) { | |
callback(new Error("val2 required")); | |
} else { | |
callback(null, val1 + val2) | |
} | |
} | |
// Promise implementation of the above callback | |
function promiseAsyncFunction(val1, val2) { | |
return new Promise(function(resolve, reject) { | |
if (!val1) { | |
reject(new Error("val1 required")); | |
} else if (!val2) { | |
reject(new Error("val2 required")); | |
} else { | |
resolve(null, val1 + val2) | |
} | |
}) | |
} | |
// This is how it would be written in an async function. Notice it is actually | |
// exactly the same as a synchronous version of the function, but the async | |
// keyword means it implicitly retuns a promise | |
async function asyncFunction(val1, val2) { | |
if (!val1) { | |
throw new Error("val1 required"); | |
} | |
if (!val2) { | |
throw new Error("val2 required"); | |
} | |
return val1 + val2; | |
} | |
// Or we can cheat and "promisify" the callback so we don't have | |
// to reimplement the logic | |
function promisifiedAsyncFunction(val1, val2) { | |
return new Promise(function(resolve, reject) { | |
callbackAsyncFunction(val1, val2, function(err, data) { | |
if (err) { | |
reject(err); | |
} else { | |
resolve(data); | |
} | |
}); | |
}); | |
} | |
/* | |
* Next, we will implement some functions to consume our asynchronous functions. | |
*/ | |
// This function consumes the callbacks | |
// Notice that at some points err and data are overwritten | |
// This is referred to by javascript developers as "callback hell" | |
function callbackConsumer(val1, val2, cb) { | |
callbackAsyncFunction(val1, val2, function(err, data) { | |
if (err) { | |
// handle error | |
} else { | |
callbackAsyncFunction(data, val2, function(err, data) { // IMPORTANT: err and data shadow the above scope | |
if (err) { | |
// handle error | |
cb(err) | |
} else { | |
// process data | |
cb(null, data) | |
} | |
}); | |
} | |
}); | |
} | |
// This is the same logic implemented in promises | |
// Notice that the "data" variable is not overwritten but actually is in a new scope | |
// Variables declared in the first .then handler are not available in subsequent handlers | |
function promiseConsumer(val1, val2) { | |
return promiseAsyncFunction(val1, val2) | |
.then(function(data) { | |
return promiseAsyncFunction(data, val2); | |
}) | |
.then(function(data) { // this is a new data object, it is not overwriting the old one | |
// process data | |
if (somethingBad) { | |
// promise functions throw just like their synchronous counterparts, | |
// but errors are handled in the .catch handler rather than a try/catch block | |
throw new Error("something bad happened") | |
} | |
return processedData; | |
}) | |
.catch(function(err) { // catch removes conditional error handling and decreases human error | |
// handle error | |
}) | |
} | |
// This is the new Async/Await style consumer | |
// Any function that returns a promise can be awaited, and all async functions return promises no matter what | |
async function asyncPromiseConsumer(val1, val2) { | |
try { // async functions handle errors in normal try/catch blocks | |
// call asynchronous functions just like synchronous functions | |
// this can only be done in functions explicitly marked async | |
let data = await promiseAsyncFunction(val1, val2); | |
data = await promiseAsyncFunction(data, val2); | |
// process data | |
if (somethingBad) { | |
// async functions throw just like their synchronous counterparts | |
// errors will go to the catch block just like synchronous programming | |
throw new Error("something bad happened") | |
} | |
return processedData; // This implicitly returns as a promise | |
} catch (err) { | |
// handle error | |
} | |
} |
Subscribe via RSS