Asynchronous

(Callback, Promise, async/await)

In general, we understand what are synchronous and asynchronous meaning. However, for asynchronous, we don’t understand how to use these functions to deal with it even we know the meaning. This is a purpose for using the simple sentence to make newbies understand.

callback()

The normal callback function for simple example is as following. We use delayFunc() to be a parameter and put into the mainFunc(). The mainFunc() will implement in order. So the output is shown in the code block.

function delayFunc() {
  console.log('This delay function is done.');
}
function mainFunc(callback) {
  console.log('This is main start.');
  callback();
  console.log('This is main end.');
}

mainFunc(delayFunc);

/* output :
This is main start.
This delay function is done.
This is main end.*/

We assume the delayFunc1(), delayFunc(2) and delayFunc(3) are asynchronous functions and need time to finish. The example is as following. In mainFunc(), you can see we set the function as a nest so that the mainFunc() can implement in order.(delayFunc1delayFunc2delayFunc3)

function delayFunc1(callback) {
  setTimeout(() => {
    console.log('delayFunc1 is done');
    callback();
  }, 3000);
}
function delayFunc2(callback) {
  setTimeout(() => {
    console.log('delayFunc2 is done');
    callback();
  }, 2000);
}

function delayFunc3(callback) {
  setTimeout(() => {
    console.log('delayFunc3 is done');
    callback();
  }, 1000);
}

function mainFunc(callback) {
  delayFunc1(() => {
    delayFunc2(() => {
      delayFunc3(() => {
        callback();
      });
    });
  });
}
mainFunc(() => {
  console.log('All function is done.');
});

/* delayFunc1 is done
delayFunc2 is done
delayFunc3 is done
All function is done.*/

However, it is hard to review these codes if the nest becomes more complicated. So, we use the promise function to improve it.

Promise

Promise can allow you to associate handlers with asynchronous action's eventual success value or failure reason. There are three states in Promise :

  1. pending: initial state, means it is not finished treatment.

  2. resolve: it means success and finishing the implementation.

  3. reject: something is a failure or error has occurred.

//Instance
new Promise((resolve, reject) => {})

When using the Promise, you have to create the instance first via new Promise() and set an argument for this instance that can use it to control the process by .then(). There is a simple example as following:

var error = false;
var promiseInstance = new Promise((resolve, reject) => {
  setTimeout(() => {
    if (error) {   
      return reject('error happen');
    }

    console.log('Work(1) is done');
    resolve(); //Can return value or object, ex. resolve(1)
  }, 3000);
});

promiseInstance
  .then(() => {
    console.log('Work(2) is done');
  })
  .then(() => {
    console.log('All works is done');
  })
  .catch((error) => {
    console.warn(error);
  });

/* Work(1) is done
   Work(2) is done
   All works is done */

Explanation:

First, we set a new instance of PromiseInstance and include the asynchronous process that needs 3 seconds to display the result. Then, PromiseInstance.then(…)can implement the next work or catch the error, and so on. When using resolve(), Promise state is success and can return the object or value. And it will be transferred to the next process to utilize. However, it will be no longer to implement .then() if Promise get the reject() and error has occurred that will go to .catch to display the error message.

However, we may have two or three functions that are asynchronous and want to execute in order. You need to make another promise instance to insert one .then() like the example as following. So, it can display in order. The example is as following:

var error = false;
var promiseInstance = new Promise((resolve, reject) => {
  setTimeout(() => {
    if (error) {
      return reject('error happen');
    }

    console.log('Work(1) is done');
    resolve();
  }, 3000);
});

promiseInstance
  .then(() => {
    console.log('Work(2) is done');
  })
  .then(() => {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        console.log('Work(3) is done');
        resolve();
      }, 3000);
    });
  })
  .then(() => {
    console.log('All works is done');
  })
  .catch((error) => {
    console.warn(error);
    console.log(promiseInstance);
  });

/* Work(1) is done
   Work(2) is done
   Work(3) is done
   All works is done */
Promise.all()

In general, you have a lot of asynchronous functions that need to execute and could set a lot of promise. Promise.all() can confirm that all of them are executed and go .then() to finish this code. The example is as following:

var error = false;
var promiseInstance1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    if (error) {
      reject('error happen(1)');
    }

    console.log('Work(1) is done');
    resolve();
  }, 3000);
});

var promiseInstance2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    if (error) {
      reject('error happen(2)');
    }
    console.log('Work(2) is done');
    resolve();
  }, 1000);
});

Promise.all([promiseInstance1, promiseInstance2])
  .then(() => {
    console.log('Work(3) is done');
  })
  .then(() => {
    console.log('All works is done');
  })
  .catch((error) => {
    console.warn(error);
  });

/* Work(2) is done
   Work(1) is done
   Work(3) is done
   All works is done */

However, you can see the results are not in order. Promise.all() just make sure that it executes all of them but not keep them in order. So, you have to write some code to fix them (it's not the purpose of this article for deep research). The figure shows the process of Promise.all() .

async/await

async/await is just a syntax sugar because it includes the syntax of Promise. First, you have to know if this function includes the Promise or not, then you can use async/await. Then, declare the function is an async function as the following example and write the await before the asynchronous function. When main() is executed, it will wait for the asynchronous function to finish and then go to the next. This syntax has contained the concept of Promise. You need to learn the Promise first before utilizing this syntax.

There is one thing you have to know about the error message. You have to use the try {…} catch {…} to get the error message cause it is included in Promise and use .catch() to get the error. And you need to write the catch inside the async function or it will not get the message if you write it outside.

var error = false;
function promiseInstance1() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (error) {
        reject('error happen(1)');
      }
      console.log('Work(1) is done');
      resolve();
    }, 3000);
  });
}

function promiseInstance2() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (error) {
        reject('error happen(2)');
      }
      console.log('Work(2) is done');
      resolve();
    }, 1000);
  });
}


async function main() {
  try {
    await promiseInstance1();
    await promiseInstance2();

    console.log('Work(3) is done');
    console.log('All works is done');
  } catch {
    console.log('error');
  }
}

main();

/* Work(1) is done
   Work(2) is done
   Work(3) is done
   All works is done */

(Please leave a message to me if there are some wrong concepts in this article.)

Ref:

https://medium.com/@yining1204/javascript%E4%B8%AD%E7%9A%84%E9%9D%9E%E5%90%8C%E6%AD%A5%E8%99%95%E7%90%86%E6%A9%9F%E5%88%B6-promise-9b7c87603e47

https://eyesofkids.gitbooks.io/javascript-start-es6-promise/content/contents/ch3_promise_a_plus.html

https://cnodejs.org/topic/5c0ddfbe7ec239239ff5533a

https://segmentfault.com/a/1190000040514839