DEV Community

Cover image for Async programming basics every JS developer should know

Async programming basics every JS developer should know

Siwalik Mukherjee on June 14, 2018

This article is aimed at people starting out with asynchronous coding in javascript so we would keep things simple by avoiding big words, arrow fu...
Collapse
zeke profile image
Zeke Hernandez

As someone who doesn't regularly code JS, this was a very helpful primer for these features. Thanks!

Collapse
siwalikm profile image
Siwalik Mukherjee Author

Glad to hear that Zeke! 🙂

Collapse
vonheikemen profile image
Heiker

Nice article. Good job.

One thing that is worth mention is that callback hell can be avoided even if you are "stuck" in an environment that doesn't support promises or async/await. You can always use a function reference instead of an anonymous function.

The game example could look like this:

// ...

var levelThreeReached = function (value) {
  console.log('Level Three reached! New score is ' + value);
}

function levelTwoReached (value) {
  console.log('Level Two reached! New score is ' + value);
  levelThree(value, levelThreeReached);
}

function levelOneReached(value) {
  console.log('Level One reached! New score is ' + value);
  levelTwo(value, levelTwoReached);
}

function startGame() {
  var currentScore = 5;
  console.log('Game Started! Current score is ' + currentScore);
  levelOne(currentScore, levelOneReached);
}
Collapse
nempet profile image
Nemanja Petrovic

In the last example, do you really need

 return promise = new Promise(function(resolve) {
        resolve(newScore);
    });

in each function, or you can simply return?

return value + {integer};

By the way, great article!

Collapse
joepvl profile image
Joep van Liempd • Edited on

I think the author was returning those Promises explicitly here to illustrate the point of being able to "get the value out of" the Promise using the await keyword, without having to jump through the normal .then(fn) hoops. An alternative (usually considered to be more idiomatic) way to write the return statements would have been return Promise.resolve(newScore);.

However, without any "real" async logic in those functions, you could indeed simply return and it would preserve the current behaviour; you can await a regular value just fine (i.e. let x = await 5 is valid syntax). As far as I'm aware it can potentially affect the order in which things are processed within the current iteration of the event loop, but that won't observably influence things in most cases, including this one.

For completeness' sake, we could take things one step further by making the level functions async as well. An async function will always return a Promise implicitly, so the following are functionally equivalent:

async function levelOne(value) {
  return value + 5;
}

function levelOne(value) {
  return Promise.resolve(value + 5);
}

This way, we can leave the explicit Promises out, but still benefit from the asynchronous flow.

To make the asynchronous behaviour clearer, we could introduce a "wait" function as well, which does nothing more than resolve the Promise it returns after the passed amount of milliseconds:


// note: there is no need to mark this function `async`,
// as we need to explicitly return a `Promise` anyway in
// order to make use of `resolve`
function wait(ms) {
  return new Promise(function(resolve) {
    setTimeout(resolve, ms);
  });
}

async function levelOne(value) {
  await wait(500);
  return value + 5;
}

async function levelTwo(value) {
  await wait(500);
  return value + 10;
}

async function levelThree(value) {
  await wait(500);
  return value + 30;
}

async function startGame() {
  let currentScore = 5;
  console.log('Game Started! Current score is ' + currentScore);
  currentScore = await levelOne(currentScore);
  console.log('You have reached Level One! New score is ' + currentScore);
  currentScore = await levelTwo(currentScore);
  console.log('You have reached Level Two! New score is ' + currentScore);
  currentScore = await levelThree(currentScore);
  console.log('You have reached Level Three! New score is ' + currentScore);
}

startGame();

If you run this code (e.g. paste it into your console and press enter), you'll see that it runs just as the code in the article, the difference being it has 500ms pauses in between the log messages (the number 500 coming from the value that is passed to wait in each case).

Note that if you were to remove the await keywords in the level functions, that would take away the 500ms pause in that spot. The wait function would still be called, but the engine is not instructed to wait for the returned Promise to resolve before continuing, so it will just move on to the next statement immediately.

Collapse
siwalikm profile image
Siwalik Mukherjee Author

I couldn't have explained this question better @Joep 😀Thank you 🙏

Thread Thread
joepvl profile image
Joep van Liempd

My pleasure!

Collapse
alrobilliard profile image
Andrew Robilliard

I've been struggling to wrap my head around promises and this article made it very clear with your example. Time to go write something with async/await 😀

Collapse
siwalikm profile image
Siwalik Mukherjee Author

Thank you Andrew, good luck ✌️

Collapse
rogercoder profile image
Roger Hand

To anyone exploring asynchronous Javascript programming, also take a look at Observables, in the Rxjs library.

Collapse
alwynschoeman profile image
Alwyn Schoeman

Your callback examples are not async or am I missing something?

Collapse
siwalikm profile image
Siwalik Mukherjee Author • Edited on

Hi Alwyn, the examples are asynchronously executed indeed, just that for the simplicity's sake our example level functions call the callbacks instantly. I have not introduced a setTimeout or a ajax call inside the functions but you can play around and see that even if you do, they work perfectly.

Here's an example demonstrating that

function levelOne(value, callback) {
  var newScore = value + 5;
  // making API call to slower server with 5 sec. delay
  fetch('https://www.mocky.io/v2/5b5c746332000081004262fd?mocky-delay=5000ms')
    .then(function (response) {
      return response.json();
    })
    .then(function (myJson) {
      console.log(`// delay on async api call for ${myJson.delay}`);
      callback(newScore);
    });
  // p.s. although setTimeout makes your code behave like async(), it is not.
  // read more => https://softwareengineering.stackexchange.com/q/194580/
}

function startGame() {
  var currentScore = 5;
  console.log(`Game Started! Current score is ${currentScore}`);
  levelOne(currentScore, function (levelOneReturnedValue) {
    console.log(`Level One reached! New score is ${levelOneReturnedValue}`);
  });
  console.log(`Hi, I'm a rebel and I don't wait for levelOne`);
}

startGame();

// Game Started! Current score is 5
// Hi, I'm a rebel and I don't wait for levelOne

// // delay on async api call for 5 seconds
// Level One reached! New score is 10
Collapse
sodic profile image
Filip Sodić • Edited on

I realize I'm a bit late to the discussion, but, since this is a fairly popular article and I believe many will be using it as a reference in the future, I chose to write this comment anyway.

Also, keep in mind, I am far from a JS expert, it is entirely possible for me to get something wrong or misinterpret the original article. Then again, if I misinterpreted it, who knows how many others might misinterpret it as well.

With all that said, Alwyn is 100% percent right, the callbacks in the article are not asynchronous and, if you think about it, they really shouldn't be. As stated in the article, a callback is simply a function passed as an argument to another function. It's just like any other argument a function can get. It makes no sense for a callback to be magically asynchronously called just by being there. We would not expect an argument of type Number to make anything async, so why should a function be any different?

Siwalik, all that is called asynchronously in the code you provided as an answer to Alwyn's question is the first argument of the setTimeout function. This function creates an event and attaches the arrow function to it. This arrow function is then called after the event is settled (5 seconds later). The functions startGame, levelOne and callback are all called synchronously. The only thing making the code appear asynchronous is the event you created with setTimeout and this has nothing to do with callbacks.

A good example of proving the synchronous and disproving the asynchronous nature of your original functions is this one:

function levelOne(value, callback) {
    var newScore = value + 5;
    callback(newScore);
    console.log('After callback');
}

function startGame() {
    var currentScore = 5;
    console.log('Game Started! Current score is ' + currentScore);
    levelOne(currentScore, function(levelOneReturnedValue) {
        console.log('Level One reached! New score is ' + levelOneReturnedValue);

        // some useless job to cause waiting
        var a;
        for(var i = 0; i < 1000000000; i++){
            a += i;
        }

        console.log('Done with callback');
    });
    console.log('After levelOne');
}

startGame();

Notice how 'After callback' and 'Done with callback' are printed before 'After levelOne', proving that the callback call is indeed synchronous.

Turns out this 'callbacks being asynchronous' thing is a common misconception. Take a look at this stack overflow thread for more information.

Again, sorry if you knew all of this and I just misunderstood your words.

Thread Thread
siwalikm profile image
Siwalik Mukherjee Author • Edited on

Hi @Filip, I appreciate you for taking the time and explaining this in detail.

I realise setTimeout isn't asynchronous but I've naively used it to mock how an async call would actually behave, didn't realise people starting out might get confused thinking setTimeout has to do anything with async.

Update: I went up and replaced the setTimeout I wrote in @Alwin's reply with an actual API call with delay, for correct depiction of javascript's working. :)

Collapse
dannypsnl profile image
林子篆

Well done!

Collapse
siwalikm profile image
Siwalik Mukherjee Author

Thank you 🙌

Collapse
lmbarr profile image
Luis Miguel

Nice article, very succinct and concise.

Collapse
siwalikm profile image
Siwalik Mukherjee Author

Yay! Learnt a new word, now I have to use it! 🎉🎉
Glad you found it succinct Luis 🤓

Collapse
usuario001 profile image
José Manuel

Good and perfect!

Collapse
siwalikm profile image
Siwalik Mukherjee Author

Thanks José 🙌

Collapse
lucasmonstro profile image
Lucas Silva

Do not forget to write code in es7+ way :-D Great article.

Collapse
jochemstoel profile image
Jochem Stoel

Thank you for this well written article. I am sure many will find it useful.

Collapse
siwalikm profile image
Siwalik Mukherjee Author

Thanks Jochem! :)

Collapse
binarydigit profile image
BinaryDigit

This is really great and succinct! I understand it...I think 😂

Collapse
ssunil profile image
Sunil Shenoy

You have explained this really well. 👌

Collapse
siwalikm profile image
Siwalik Mukherjee Author

Thank you Sunil 🙌

Collapse
tucq88 profile image
Tu Chu

Pretty decent post. Actually I've never see any cleaner guide to resolve callback helll like this. Kudos!

Collapse
siwalikm profile image
Siwalik Mukherjee Author

Appreciate that Tu! Thanks 🙌

Collapse
varsanyid profile image
varsanyid

Thanks for the article! However, a high-order function is a function that accepts another function as its parameter or returns one. A callback function is a function that gets passed as a parameter.

Collapse
siwalikm profile image
Siwalik Mukherjee Author • Edited on

Hey varsanyid, nice catch! 🙌Corrected the typo, thanks for pointing 😀

Collapse
juniorbatistard profile image
Junior Batista

Really useful! Thank you!

Collapse
siwalikm profile image
Siwalik Mukherjee Author

I'm happy that you found this helpful.

Collapse
roqkabel profile image
dev-obed

I love this post .. Everything is clear. Thanks

Collapse
surya458 profile image
Surya Tanuku

cool. Thanks