Home

Asynchronous Javascript

Asynchronous - more than one code thread can be run at the same time.
Synchronous - (default) code runs as one thread and one command has to finish before the next one can start.

...you can also take a look at an MDN document on async/await (Making asynchronous programming easier with async and await).

...and also another document on (Choosing the right approach).

The async keyword is used to write functions that handle asynchronous actions.
We wrap our asynchronous logic inside a function prepended with the async keyword.
Then, we invoke that function. Eg...

async function myFunc() {
   // Function body here
};

myFunc();


OR you can say...

const myFunc = async () => {
   // Function body here
};

myFunc();


async functions always return a promise.
This means we can use traditional promise syntax, like .then() and .catch() with our async functions.
An async function will return in one of three ways:-

...If there’s nothing returned from the function, it will return a promise with a resolved value of undefined.
...If there’s a non-promise value returned from the function, it will return a promise resolved to that value.
...If a promise is returned from the function, it will simply return that promise.

For example...

async function fivePromise() { 
  return 5;
}
 
fivePromise()
.then(resolvedValue => {
    console.log(resolvedValue);
  })  
// Prints 5
In the example above, even though we return 5 inside the function body, what’s actually returned when we invoke fivePromise() is a promise with a resolved value of 5.



Therefore, a promise that may be written like...

function nativePromise(){
  return new Promise((resolve, reject) => {
      resolve('yay!');
  })
}
Using async could be written like...
async function asyncPromise(){
  return 'yay!';
}
Because
...If there’s a non-promise value returned from the function, it will return a promise resolved to that value.



A larger example could be...

function withConstructor(num){
  return new Promise((resolve, reject) => {
    if (num === 0){
      resolve('zero');
    } else {
      resolve('not zero');
    }
  });
}

async function withAsync(num) {
  return withConstructor(num);
};


withAsync(100)
  .then((resolveValue) => {
  console.log(` withAsync(100) returned: ${resolveValue}.`);
})
>//would print to the Console.log...
//withAsync(100) returned: not zero. 


withAsync(0)
  .then((resolveValue) => {
  console.log(` withAsync(0) returned: ${resolveValue}.`);
})
//would print to the Console.log...
//withAsync(100) returned: zero. 

Top

The 'await' operator

async functions are almost always used with the additional keyword await inside the function body.

The await keyword can only be used inside an async function.
await is an operator: it returns the resolved value of a promise.
Since promises resolve in an indeterminate amount of time, await halts, or pauses, the execution of our async function until a given promise is resolved.

So, for example...

Lets pretend we have a promise function (let's call it 'funcTakesTime()') that runs a few .then() commands that prints out messages as it goes and could take a bit of time to run.
And we call it in our async function...

async function myAsync() {
   let myTask = await funcTakesTime();
   console.log(`All tasks have finished now.`);
}

When run this would display...

funcTakesTime Message 1
funcTakesTime Message 2
funcTakesTime Message 3
etc...
All tasks have finished now.


Because the await command will make everything stop until all the funcTakesTime tasks have completed (it makes it run synchronously).

But if we do NOT have the await command then the result would be...

All tasks have finished now.
funcTakesTime Message 1
funcTakesTime Message 2
funcTakesTime Message 3
etc...

Because the removal of the await command will make everything run asynchronously and not wait for the other funcTakesTime tasks to complete first.

Top

Another example - library.js

This file is called library.js and it exports its functions for use by the file app.js

const shopForBeans = () => {
  return new Promise((resolve, reject) => {
	const beanTypes = ['kidney', 
        'fava', 
        'pinto', 
        'black', 
        'garbanzo'];
  setTimeout(()=>{
    let randomIndex = Math.floor(Math.random() * 5);
    let beanType = beanTypes[randomIndex];
    console.log(`I bought ${beanType} beans 
because they were on sale.`);
   resolve(beanType);
  }, 1000)
})
}

let soakTheBeans = (beanType) => {
   return new Promise((resolve, reject) => {
     console.log('Time to soak the beans.');
    setTimeout(()=>{
      console.log(`... The ${beanType} beans 
are softened.`);
      resolve(true);
      }, 1000);
  });
}

let cookTheBeans = (isSoftened) => {
  return new Promise((resolve, reject) => {
    console.log('Time to cook the beans.');
    setTimeout(()=>{
      if (isSoftened) {
        console.log('... The beans are cooked!');
        resolve('\n\nDinner is served!');
      }
    }, 1000);
  });
}

  
module.exports = {
    shopForBeans, 
    soakTheBeans, 
    cookTheBeans};

Another example - app.js

This file is called app.js and it imports its functions from the file library.js

const {shopForBeans, soakTheBeans, cookTheBeans} = 
require('./library.js');

async function makeBeans() {
  let type = await shopForBeans();
  let isSoft = await soakTheBeans(type);
  let dinner = await cookTheBeans(isSoft);
  console.log(dinner);
}

makeBeans();

The line (above)
let type = await shopForBeans();
will randomly choose a bean and then the 'resolve' part of it will return the name of the bean so it gets stored in the variable type.

Each function from library.js is run in order because of the await command.

shopForBeans() returns a random bean that is passed to
soakTheBeans() and then gets soaked. Then it is passed to
cookTheBeans() to be cooked.

The output from this (when run in 'Command Prompt' using $ node app.js) will display on the console.log the following...

I bought kidney beans they were on sale.
Time to soak the beans.
... The kidney beans are softened.
Time to cook the beans.
... The beans are cooked!

Dinner is served!


Top


The above is an example of a routine that is expected to complete successfully all the time.

But seeing as not everything does always work, the below example allows for something to fail.

With this I have used the Try/Catch routine (the link will take you to my page on Try/Catch if you want to see that as well.

Top

Using Try/Catch example - library.js

This file is called library.js and it exports its functions for use by the file app.js

// This function returns true 50% of the time.
let randomSuccess = () => {
 let num = Math.random();
 if (num < .5 ){
   return true;
 } else {
   return false;
 }
};

// This function returns a promise that 
// resolves half of the time and 
// rejects half of the time.
let cookBeanSouffle = () => {
 return new Promise((resolve, reject) => {
   console.log('Fingers crossed... Putting the 
Bean Souffle in the oven');
   setTimeout(()=>{
     let success = randomSuccess();

     // Remember to use RESOLVE and REJECT when 
     // trying to catch errors.
     if(success){
       resolve('Bean Souffle');
     } else {
       reject('Dinner is ruined!');
     }
   }, 1000);
 });
};

module.exports = cookBeanSouffle;

Using Try/Catch example - app.js

This file is called app.js and it imports its functions from the file library.js

const cookBeanSouffle = require('./library.js');

async function hostDinnerParty() {
  try {

    //lets call our routine
    let dinnerMsg = await cookBeanSouffle();

    //If it RESOLVED okay then 'dinnerMsg will
    // = 'Bean Souffle'  

    // But if it REJECTED then it will 
    // drop to the 'catch (error)' area
    // and the 'error' variable will    
    // = 'Dinner is ruined' 

    //If it RESOLVED then
    //NOTE
    // when using ${..} to display a variable
    // you MUST use `` (before the 1 key)
    // and NOT "" or ''
    console.log(`${dinnerMsg} is served!`);

  } catch (error){
    console.log(error);
    console.log("Ordering a pizza!");
  }
}

hostDinnerParty();


The first message displayed on the console.log will always be
Fingers crossed... Putting the Bean Souffle in the oven

Then, if it worked (resolved) okay you will see the message
Bean Souffle is served!

But if it failed (rejected) then you will see
Dinner is ruined!
Ordering a pizza!


Top

Two ways of using 'await'

Normally I would use...eg...

async function serveDinner() {
  const vegetablePromise = await steamBroccoli();
  const starchPromise = await cookRice();
  const proteinPromise = await bakeChicken();
  const sidePromise = await cookBeans();
  console.log(`Dinner is served. We're having 
    ${vegetablePromise}, 
    ${starchPromise}, 
    ${proteinPromise}, 
    and ${sidePromise}.`);
}

But you could also do it like this...

async function serveDinner() {
  const vegetablePromise = steamBroccoli();
  const starchPromise = cookRice();
  const proteinPromise = bakeChicken();
  const sidePromise = cookBeans();
  console.log(`Dinner is served. We're having 
    ${await vegetablePromise}, 
    ${await starchPromise}, 
    ${await proteinPromise}, 
    and ${await sidePromise}.`);
}
Because the const vegetablePromise = steamBroccoli();
does not actually run the routine (steamBroccoli), it just creates an instance of it and stores it as a variable (vegetablePromise) ready for when you actually want to use it.
So it only runs 'steamBroccoli' when it gets to the line that calls it
await vegetablePromise

My Comment
I think that you could combine asynchronous and synchronous running by using...
await vegetablePromise in one place in the program and then elsewhere just use
vegetablePromise without the 'await'.


Top