loading...

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... [Read Full]
pic
Editor guide
 

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

 

Glad to hear that Zeke! πŸ™‚

 

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);
}
 

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!

 

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.

 

I couldn't have explained this question better @Joep πŸ˜€Thank you πŸ™

 

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 πŸ˜€

 

Thank you Andrew, good luck ✌️

 

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

 

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

 

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
 

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.

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. :)

 
 

Nice article, very succinct and concise.

 

Yay! Learnt a new word, now I have to use it! πŸŽ‰πŸŽ‰
Glad you found it succinct Luis πŸ€“

 
 

Thanks JosΓ© πŸ™Œ

 

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

 

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

 

Thanks Jochem! :)

 

This is really great and succinct! I understand it...I think πŸ˜‚

 

You have explained this really well. πŸ‘Œ

 

Thank you Sunil πŸ™Œ

 

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

 

Appreciate that Tu! Thanks πŸ™Œ

 

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.

 

Hey varsanyid, nice catch! πŸ™ŒCorrected the typo, thanks for pointing πŸ˜€

 

Really useful! Thank you!

 

I'm happy that you found this helpful.

 

I love this post .. Everything is clear. Thanks

 
Code of Conduct Report abuse