JavaScript Essentials for React Native - #5 Promises, async, await

JavaScript Essentials for React Native - #5 Promises, async, await

Promises facilitate efficient handling of multiple tasks asynchronously

When we are building software, we need to make sure we do the following mentioned things smoothly :

  • Data Fetching

  • Image Loading

  • Push Notifications

  • Animations

  • Database Operations

  • Managing sensors

  • Dealing with third party APIs etc

And a lot of times, we need to manage seamless running of these tasks or combination of these tasks as we have "promised" our users of a great user experience.

To seamlessly execute these diverse tasks and ensure a consistent user experience, developers rely on a powerful JavaScript feature called Promises.

Why do we need Promises? Can we skip them?

As highlighted above, today the mobile applications have become very complex. Let's say you have to do 10 tasks : Task A, Task B ... Task J in your application. Here are the constraints:

  • Some of these tasks are dependent on each other, others independent.

  • Some of these tasks have more priority than others.

  • These tasks execute with different time on different mobile phones due to diverse range of mobile phone models in the market.

And as a software developer, you have to make sure that the software runs as you predicted, in the same way across all devices.

If you write JavaScript code without Promises i.e synchronous code, then it means you leave it to the constraints and the device and third party tasks, to decide the behavior of your application. Meaning unpredictable behavior.

Promises make software behavior more predictable, ensuring it behaves as intended.

Promise syntax

let promise = new Promise(function(resolve, reject) {
  // executor OR producing code => This code does something and takes time
  • The function passed to new Promise is called the executor.

    • When a new Promise is created, the executor runs automatically.

    • It contains the producing code responsible for generating the result.

  • The executor's arguments, resolve and reject, are callbacks provided by JavaScript.

  • When the executor obtains the result (regardless of timing), it should call one of these callbacks:

    • resolve(value) — if the job is finished successfully, providing the result value.

    • reject(error) — if an error occurs, with the error object.

  • The executor runs automatically, attempting to perform a job.

    • Upon completion, it calls resolve for success or reject for an error.
  • The promise object returned by the new Promise constructor has internal properties:

    • state — initially "pending," then changes to either "fulfilled" or "rejected."

    • result — initially undefined, changing to the value or error upon resolution.

  • Example of resolved promise

      let promise = new Promise(function(resolve, reject) {
        // the function is executed automatically when the promise is constructed
        // after 1 second signal that the job is done with the result "done"
        setTimeout(() => resolve("done"), 1000);
  • Example of rejected promise

      let promise = new Promise(function(resolve, reject) {
        // after 1 second signal that the job is finished with an error
        setTimeout(() => reject(new Error("Whoops!")), 1000);

    Consumers: then, catch

    • Promise is just a wrapper around our unpredictable task ( doing API calls, file handling etc ).

    • Now, either that task was completed or failed, this info is communicated to our Promise by resolve or reject functions.

    • And we have special functions then, catch which are consumers of our promise.

    • then ( handle success, error events )

      • Syntax:

      •   promise.then(
            function(result) { /* handle a successful result */ },
            function(error) { /* handle an error */ }
    • catch ( to handle errors only )

      • Syntax:

      •   let promise = new Promise((resolve, reject) => {
            setTimeout(() => reject(new Error("Oops!")), 1000);
          // .catch(f) is the same as promise.then(null, f)
          promise.catch(alert); // shows "Error: Oops!" after 1 second
    • finally ( clean up )

      • Syntax:

      •   new Promise((resolve, reject) => {
            /* do something that takes time, and then call resolve or maybe reject */
            // runs when the promise is settled, doesn't matter successfully or not
            .finally(() => stop loading indicator)


  • This is the modern way to work with promises.

  • await only works inside an async function.

  • A function that returns a promise is an async function. Meaning just adding this keyword outside any function, ensures that the function returns a promise.

  • Example:

      function greet() {      // 1. normal function,  returns "Hello"
         return "Hello" 
      async function greet2() { // 2. async function returns Promise
          return "Hello"
      function greet3() {     // 3. This is same as 2
          return Promise.resolve("Hello");
  • await keyword works only inside async functions

  •   let result = await promise;
  • await keyword makes JavaScript wait until that promise is settled

      async function foo() {
        let promise = new Promise((resolve, reject) => {
          setTimeout(() => resolve("done!"), 1000)
        let result = await promise; // wait until the promise resolves 
        console.log(result); // "done!"
  • add try, catch, finally blocks for error handling


In this blog, we talked about how we can make our apps more predictable and efficient using Promises. By breaking down complex tasks into small units and using Promises, developers can ensure a smoother and more reliable user experience across various scenarios.

Thanks for reading!