Explain the Javascript Async Programming with history lesson

Explain the Javascript Async Programming with history lesson

I have been training many fresher developers since the start of my career, and I happened to realize that all new NodeJS/Javascript developers rarely understand the nature of Async Programming in NodeJS and Javascript

Thomas
7 minute read

Explain the Javascript Async Programming with a history lesson

I have been training many fresher developers since the start of my career, and I happened to realize that all new NodeJS/Javascript developers rarely understand the nature of Async Programming in NodeJS and Javascript. What I mean here is the nature of it, understanding how it works as things in real life, not explaining it with Event Loop and abstract concepts that confuse others.

Why do we do async?

Just imagine one day, when you wake up in a lovely early morning, ready to get started for your working day. You put your kettle on, wait for the water to boil. After 3 minutes, you found yourself wasting a bunch of time since you should have prepared your coffee in the meantime. And you regret that you did not do it asynchronously. Back in time when I learned PHP, I forced myself into thinking like a single-thread machine. We thought that we need to learn programming language to work with machines. We were wrong, machines were built to serve humans, to learn from humans, if we can do asynchronously, machines should as well.

History of the Async Programming.

I love history. I always learn from the past to understand the evolution of humans and things. Once we understand the history, we can understand the nature of it, once we do that, we know the crux of it.

Since the very first beginning of programming, the Callback concept was introduced, so the first version of Javascript already had the Callback within its core. Later on, we adapted jQuery as the backbone of the whole Web. We got so familiar with this snippet of code with jQuery:

$("button").click(function(){ $("p").hide("slow", function(){ alert("The paragraph is now hidden"); }); });

Things seem just to get just right until we have a thousand lines of code with a single Javascript file full of callback hell and comments, hoping that comments will magically explain this mess.

fs.readdir(source, function (err, files) { if (err) { console.log('Error finding files: ' + err) } else { files.forEach(function (filename, fileIndex) { console.log(filename) gm(source + filename).size(function (err, values) { if (err) { console.log('Error identifying file size: ' + err) } else { console.log(filename + ' : ' + values) aspect = (values.width / values.height) widths.forEach(function (width, widthIndex) { height = Math.round(width / aspect) console.log('resizing ' + filename + 'to ' + height + 'x' + height) this.resize(width, height).write(dest + 'w' + width + '_' + filename, function(err) { if (err) console.log('Error writing file: ' + err) }) }.bind(this)) } }) }) } })

Our code just gradually slides to the right and disappears from our screen…………………………………………………………………………………………………………………………….. and is not yet to be found.

You can take a look at this snippet code running a sample code with callbacks:

/** * @description This is a example with 3 layer callback hell */ /** * Fake like we getting data from API which takes 1s * * @param callback */ function getData(callback) { setTimeout(() => { const data = [{ id: 1, name: "Developer A" }, { id: 2, name: "Devloper B" } ]; // Assume the data from API callback(data); }, 1000) }; /** * Fake like we are submitting the data to an API elsewhere * * @param inputData * @param callback */ function submitData(inputData, callback) { console.log('starting to submit'); setTimeout(() => { // Act like we submit the inputData to the server and takes 1s const status = randomStatus(); callback({ success: status }); }, 1000) } /** * Just a helper random the result of the submit: success or failed * * @returns {boolean} */ const randomStatus = () => [true, false][Math.floor(Math.random() * 2)]; // Main body of the example getData((data) => { const names = data.map(data => data.name); console.log(names) submitData(names, (response) => { const { success } = response; if (success === true) { console.log("This is a successful form") } else { console.log("This is a failed form") } }) }); /** * @conclusion * * Just imagine that after submit the form, * we continue to do more async processes * and the code keeps on being nested * * @see See this link for closer look at callback hell * @link http://callbackhell.com/ * */

Promises — We promise to make programming better

When the callback hell hits us so hard for so many years, we were introduced to a brand new thing called Promise in 2015. So what is Promise?

Just like what it is called. A promise. When someone gives you a promise, it is something in the future, it can succeed or fail, not so sure of how long it will take. Just like you buy a bond, you receive a promise, on paper, promising that you will get back your money in 10 years with 10 percent interest.

But how we would write this above snippet in Promises? It would be like:

/** * Asynchronous programming with Promise */ function getData() { return new Promise((resolve, reject) => { const data = [{ id: 1, name: "Developer A" }, { id: 2, name: "Devloper B" } ]; setTimeout(() => { resolve(data); }, 1000); }); } /** * Fake like we are submitting the data to an API elsewhere * * @param inputData */ function submitData(inputData) { console.log('starting to submit'); return new Promise((resolve, reject) => { // Act like we submit the inputData to the server and takes 1s const status = randomStatus(); setTimeout(() => { resolve({ success: status }); }, 1000); }); } /** * Just a helper random the result of the submit: success or failed * * @returns {boolean} */ const randomStatus = () => [true, false][Math.floor(Math.random() * 2)]; // Main body of the example getData().then(data => { const names = data.map(data => data.name); console.log(names); submitData(names).then(response => { const { success } = response; if (success === true) { console.log("This is a successful form") } else { console.log("This is a failed form") } }); });

Oh! New wine into old bottles. Even though we got a new syntax, we still suffered from the Callback hell. To be fair, the Promises solved a lot of problems, it made NodeJS and Javascript handle the Async Programming way much cleaner already with intuitive APIs. One thing missing is that it has not yet turned our programming code into a textbook, or a programming logic script in plain English so that humans can easily digest it.

Async/Await syntax

The lovely donut is missing its sprinkles. In 2017, shortly after the introduction of Promise, we got the async/await syntax came to the rescue. With async/await, we can finally rewrite the above code as followed, line by line, intuitively.

/** * Asynchronous programming with Promise */ function getData() { return new Promise((resolve, reject) => { const data = [{ id: 1, name: "Developer A" }, { id: 2, name: "Devloper B" } ]; setTimeout(() => { resolve(data); }, 1000); }); } /** * Fake like we are submitting the data to an API elsewhere * * @param inputData */ function submitData(inputData) { console.log('starting to submit'); return new Promise((resolve, reject) => { // Act like we submit the inputData to the server and takes 1s const status = randomStatus(); setTimeout(() => { resolve({ success: status }); }, 1000); }); } /** * Just a helper random the result of the submit: success or failed * * @returns {boolean} */ const randomStatus = () => [true, false][Math.floor(Math.random() * 2)]; // Main body of the example (async () => { const data = await getData(); const names = data.map(data => data.name); console.log(names); const { success } = await submitData(names); if (success === true) { console.log("This is a successful form") } else { console.log("This is a failed form") } })();

Wrap up

I hope that my little history lesson class will help you wrap your head around this important concept. Happy coding.

Async Programming
Javascript
Nodejs