Asynchronous JavaScript and Promises

Asynchronous JavaScript and Promises

Asynchronous programming is an essential technique in JavaScript that allows your code to run in the background without blocking the execution of other code. This is in contrast to synchronous JavaScript, where tasks are executed one at a time. For example, when you click a button on a web page, you don't want the entire page to reload. Instead, you want the button to be disabled and a loading indicator to be displayed. Once the task is complete, the page can be updated and the button can be re-enabled.

There are two main ways to write asynchronous JavaScript code: Callbacks and Promises.

Callbacks

JavaScript functions are executed in the sequence they are called. Not in the sequence they are defined.

function myFirst(){
    console.log("I am first");
}
function mySecond(){
    console.log("I am second");
}
mySecond();
myFirst();

The above example finally gives the output -

I am second 
I am first

Sometimes you would like to have better control over when to execute a function.

Suppose you want to do a calculation, and then display the result.

You could call a calculator function (myCalculator), save the result, and then call another function (myDisplayer) to display the result:

function myDisplayer(some) {
  console.log(some);
}

function myCalculator(num1, num2) {
  let sum = num1 + num2;
  return sum;
}

let result = myCalculator(5, 5);
myDisplayer(result);

Output:

10

Or, you could call a calculator function (myCalculator), and let the calculator function call the display function (myDisplayer):

function myDisplayer(some) {
  console.log(some);
}

function myCalculator(num1, num2) {
  let sum = num1 + num2;
  myDisplayer(sum);
}

myCalculator(5, 5);

Output:

10

The problem with the second example above, is that you have to call two functions to display the result.

The problem with the third example, is that you cannot prevent the calculator function from displaying the result.

Now it is time to bring callback.

Callbacks are functions that are passed to another function as an argument. The callback function is called when the other function is finished executing.

function myDisplayer(some) {
  console.log(some);
}

function myCalculator(num1, num2, myCallback) {
  let sum = num1 + num2;
  myCallback(sum);
}

myCalculator(5, 5, myDisplayer);

In the example above, myDisplayer is called a callback function because it is passed to myCalculator() as an argument. Here, you can call the calculator function (myCalculator) with a callback (myCallback), and let the calculator function run the callback after the calculation is finished.

Output:

10

In the real world, callbacks are most often used with asynchronous functions.

A typical example is JavaScript setTimeout().

function myFunction() {
  console.log("I am running...");
}
setTimeout(myFunction, 3000);
console.log("Hello JavaScript");
Hello JavaScript
I am running...

In the above example, myFunction is used as a callback.

3000 is the number of milliseconds before time-out, so myFunction() will be called after 3 seconds but the other code (console.log("Hello JavaScript"); )will be run without blocking its (setTimeout()) execution.

With asynchronous programming, JavaScript programs can start
long-running tasks, and continue running other tasks in parallel.
But, asynchronus programmes are difficult to write and difficult to debug.
Because of this, most modern asynchronous JavaScript methods don't use 
callbacks. Instead, in JavaScript, asynchronous programming is solved 
using Promises instead.

Promises

Promises are a powerful tool for handling asynchronous operations in JavaScript. They can help to make your code more readable and easier to maintain. If the promise is successful, it will produce a resolved value, but if something goes wrong then it will produce a reason why the promise failed.

Syntax:

let myPromise = new Promise(function(myResolve, myReject) {
  myResolve(); // when successful
  myReject();  // when error
});

myPromise.then(
  function(value) { /* code if successful */ },
  function(error) { /* code if some error */ }
);

A JavaScript Promise object can be:

  • Pending - the result is undefined.

  • Fulfilled - the result is a value.

  • Rejected - the result is an error object.

Example 1:

function myDisplayer(some) {
   console.log(some);
}

let myPromise = new Promise(function(myResolve, myReject) {
  let x = 0;
  if (x == 0) {
    myResolve("OK");
  } else {
    myReject("Error");
  }
});

myPromise.then(
  function(value) {myDisplayer(value);},
  function(error) {myDisplayer(error);}
);

Output:

OK

Example 2:

const myPromise = new Promise(function(myResolve, myReject) {
  setTimeout(function(){ 
      myResolve("I am running after 3 seconds..."); 
   }, 3000);
});

myPromise.then(function(value) {
  console.log(value);
});
I am running after 3 seconds...

JavaScript Async/Await

The keyword async before a function makes the function return a promise:

Example 1:

async function myFunction() {
  return "Hello";
}

Example 2:

function myDisplayer(some) {
  console.log(some);
}

async function myFunction() {
   return "Hello";
}

myFunction().then(
  function(value) {myDisplayer(value);}
);

Output:

Hello

The await keyword can only be used inside an async function.

/* The await keyword makes the function pause the execution 
and wait for a resolved promise before it continues */
async function myDisplay() {
    let myPromise1 = new Promise(function(resolve) {
        setTimeout(() => {
            resolve("I am first code");
        }, 3000);
    });
    let myPromise2 = new Promise(function(resolve) {
        resolve("I am second code");
    });
    let value1 = await myPromise1;
    let value2 = await myPromise2;
    console.log(value1);
    console.log(value2);
 }

myDisplay();
I am first code
I am second code

In the above example, myDisplay() function pauses the execution for myPromise1 to be resolved. After myPromise1 , myPromise2 will be resolved.