LET'S LEARN TOGETHER. THE BEAUTIFUL THING ABOUT LEARNING IS NOBODY CAN TAKE IT AWAY FROM YOU.

Understanding JavaScript Promises


Introduction to Promises

Dealing with asynchronous code? Promises is the way to deal with it without writing multiple callbacks. Promises were standardized and introduced in ES2015, but in ES2017 they have been superseded by async function. Understanding of Promises is the foundation of learning Async functions.

Way Promises work

Once a promise has been called, it always start with Pending state. At this point, caller function waits for the promise function to finish it's job and then return either in Resolved or Rejected state. But as we know Javascript executes in asynchronous mode, so caller function will still continue doing it's job in parallel with promise doing it's job.

Creating Promises

The Promise API exposes a constructor which you can use to create Promise -

const printSomething = (isValid) => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (isValid) {
        console.log('Yes I am valid');
        resolve();
      } else {
        console.log('Sorry I am invalid');
      }
    }, 1000);
  });
}

Here promise is checking a the passed Boolean value isValid. If the isValid is true, then promise is returning resolved promise , other returning rejected promise.

Promise provides resolve and reject to return values to the calling function in case of success or error respectively.

Consuming Promises

Let's now consume the promise with below code -

const printSomething = (isValid) => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (isValid) {
        console.log('Yes I am valid');
        resolve();
      } else {
        console.log('Sorry I am invalid');
      }
    }, 1000);
  });
}

printSomething(true)
  .then(() => printSomething(true))
  .then(() => printSomething(false));

Here I am calling printSomething function which is returning Promise either in Resolve or Reject state based on the function parameter isValid.

Here is the outcome -
Yes I am valid
Yes I am valid
Sorry I am invalid

You can execute the code here.


Chaining Promises

You can chain promises by returning result from one promise and then using the same in the next promise.

const printSomething = (name, textToAdd) => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      var retValue = '';
      if (name != null) {
        retValue = name + ' ' + textToAdd;
      } else {
        retValue = textToAdd;
      }
      console.log(retValue);
      resolve(retValue);
    }, 1000);
  });
}

printSomething(null, 'Hello')
  .then((result) => printSomething(result, 'Sudipta'))
  .then((result) => printSomething(result, 'Deb'));

As you can see in the above example, I am using the result from the first promise which is "Hello" and the using the same in the second promise to append "Sudipta" with "Hello", thus returning "Hello Sudipta". Finally using this in the third promise to append "Deb", this returning "Hello Sudipta Deb"

Here is the result
Hello
Hello Sudipta
Hello Sudipta Deb

You can execute the code here.


Handling Errors

Handling error can be done by either returning Promise is reject mode or throwing an error.
Let's understand the below example with Reject returning an error.

const printSomething = (name, textToAdd) => {
  return new Promise((resolve, reject) => {
    try {
      setTimeout(() => {
        if (textToAdd == null) {
          reject(new Error('textToAdd can not be null'));
        } else {
          var retValue = '';
          if (name != null) {
            retValue = name + ' ' + textToAdd;
          } else {
            retValue = textToAdd;
          }
          console.log(retValue);
          resolve(retValue);
        }
      }, 1000);
    } catch (e) {
      reject(e);
    }
  });
}

printSomething(null, 'Hello')
  .then((result) => printSomething(result, 'Sudipta'))
  .then((result) => printSomething(result, 'Deb'))
  .then((result) => printSomething(result, null))
  .then((result) => printSomething(result, 'From Canada'))
  .catch(err => console.log('Error occured: ' + err.message));

In the above example, I am returning Reject promise with new Error. Then using the same with catch statement while consuming the Promise. Here is the output -

Hello
Hello Sudipta
Hello Sudipta Deb
Error occured: textToAdd can not be null

You can execute the code here.
Now let's modify the same example, where instead of returning Reject promise, I will throw the error. This is helpful to handle runtime error.
const printSomething = (name, textToAdd) => {
  return new Promise((resolve, reject) => {
    try {
      setTimeout(() => {
        if (textToAdd == null) {
          throw "textToAdd can not be null";
        } else {
          var retValue = '';
          if (name != null) {
            retValue = name + ' ' + textToAdd;
          } else {
            retValue = textToAdd;
          }
          console.log(retValue);
          resolve(retValue);
        }
      }, 1000);
    } catch (e) {
      reject(e);
    }
  });
}

printSomething(null, 'Hello')
  .then((result) => printSomething(result, 'Sudipta'))
  .then((result) => printSomething(result, 'Deb'))
  .then((result) => printSomething(result, null))
  .then((result) => printSomething(result, 'From Canada'))
  .catch(err => console.log('Error occured: ' + err.message));

One important fact is that reject DOES NOT terminate control flow like a return statement does. In contrast throw does terminate control flow.

Let's have the below code with reject

const printSomething = (name, textToAdd) => {
  return new Promise((resolve, reject) => {
    try {
      setTimeout(() => {
        if (textToAdd == null) {
          reject(new Error('textToAdd can not be null'));
        } else {
          var retValue = '';
          if (name != null) {
            retValue = name + ' ' + textToAdd;
          } else {
            retValue = textToAdd;
          }
          console.log(retValue);
          resolve(retValue);
        }
      }, 1000);
    } catch (e) {
      reject(e);
    }
  });
}

printSomething(null, 'Hello')
  .then((result) => printSomething(result, 'Sudipta'))
  .then((result) => printSomething(result, 'Deb'))
  .then((result) => printSomething(result, null))
  .then((result) => printSomething(result, 'From Canada'))
  .catch(err => console.log('Error occured: ' + err.message))
  .then(() => console.log('I will still execute even after the error'));

Output:
"Hello"
"Hello Sudipta"
"Hello Sudipta Deb"
"Error occured: textToAdd can not be null"
"I will still execute even after the error"

You can execute the code here. Here you can see that catch and then statement are getting executed even after reject promise return.

Now let's have the same code with throw statement

const printSomething = (name, textToAdd) => {
  return new Promise((resolve, reject) => {
    try {
      setTimeout(() => {
        if (textToAdd == null) {
          throw "textToAdd can not be null";
        } else {
          var retValue = '';
          if (name != null) {
            retValue = name + ' ' + textToAdd;
          } else {
            retValue = textToAdd;
          }
          console.log(retValue);
          resolve(retValue);
        }
      }, 1000);
    } catch (e) {
      reject(e);
    }
  });
}

printSomething(null, 'Hello')
  .then((result) => printSomething(result, 'Sudipta'))
  .then((result) => printSomething(result, 'Deb'))
  .then((result) => printSomething(result, null))
  .then((result) => printSomething(result, 'From Canada'))
  .catch(err => console.log('Error occured: ' + err.message))
  .then(() => console.log('I will still execute even after the error'));

Output:

"Hello"
"Hello Sudipta"
"Hello Sudipta Deb"
"Uncaught textToAdd can not be null (line 6)"

You can execute the code here. You see this time the catch and then statements are not getting executed. The reason is that throw statement terminate the control flow.


Promise.all()

Promise.all() will return a promise when all the promises are resolved. If one of the promise is failed, it will reject immediately with the error message from the failed promise irrespective of whether other promises are resolved or not.

Let's have this below example: 

var returnDouble = (value) => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (value > 0) {
        resolve(value * 2);
      } else {
        reject(resolve);
      }
    }, 1000);
  });
};

var promisesToMake = [returnDouble(20), returnDouble(10), returnDouble(5)];
var finalPromise = Promise.all(promisesToMake);

finalPromise.then(function (results) {
  console.log('Final Result: ' + results);
});

Output:
"Final Result: 40,20,10"
You can execute the code here. With promise.all(), instead of three different returns, it will return one single promise once all the promises are resolved.

Now let's forcefully reject one promise in the below example -

var returnDouble = (value) => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (value > 0) {
        resolve(value * 2);
      } else {
        reject(resolve);
      }
    }, 1000);
  });
};

var promisesToMake = [returnDouble(20), returnDouble(0), returnDouble(5)];
var finalPromise = Promise.all(promisesToMake);

finalPromise
  .then((results) => console.log('Final Result: ' + results))
  .catch(err => console.log('Error occured'));

Output:
"Error occured"
You can execute the code here. In this example, the second call is failed, even though the first and third call is successful. But still the final result is failure.


Promise.race()

Promise.race() runs as soon as one of the promises you pass to it resolves, and it runs the attached callback just once with the result of the first promise resolved.
Here is the example 

var returnDouble = (value,time) => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (value > 0) {
        resolve(value * 2);
      } else {
        reject(resolve);
      }
    }, time);
  });
};

var promisesToMake = [returnDouble(20,100), returnDouble(200,50), returnDouble(5,500)];
var finalPromise = Promise.race(promisesToMake);

finalPromise
  .then((results) => console.log('Final Result: ' + results))
  .catch(err => console.log('Error occured'));

Output:
"Final Result: 400"
You can execute the code here.

Further Study




Share:

2 comments:

Follow Me

Enter your email address:

Delivered by FeedBurner

Popular Posts

Labels

Salesforce (105) Apex (45) admin (27) visualforce (21) ADM (20) dev 501 (19) integration (18) learn salesforce (18) 501 (16) SOAP (13) lightning (12) tutorial (11) Certification. (9) javascript (9) Certification (7) Trigger (7) test class (7) unit testing (7) Advanced Admin (6) Sharing and Visibility (6) design pattern (6) developer (6) report (6) salesforce release (6) security (6) trailhead (6) Advanced Apex (5) Kitchener Developer Group (5) New Features (5) SOQL (5) css (5) dashboard (5) debug (5) formula (5) mobile (5) service cloud (5) solution management (5) use case (5) JSON (4) Lightning Experience (4) Salesforce DX (4) WebSphere (4) best practice (4) cast iron (4) component (4) deployment (4) github (4) html (4) polymer (4) profiles (4) responsive (4) tdd (4) ui (4) Architect (3) Live Chat (3) Online Event (3) Opportunity (3) Performance (3) Products (3) REST (3) Role (3) Sales Cloud (3) Scratch Org (3) Study Notes. (3) Summer15 (3) Tips (3) Web Technology (3) dynamic apex (3) event (3) license (3) map (3) mapbox (3) singleton (3) version controlling (3) Asynchronous callout (2) Bulkify (2) Data Architecture and Management Certification (2) Devops (2) Distributed Version Controlling (2) ES6 (2) Eclipse (2) Einstein (2) Enterprise Territory Management (2) Financial Services Cloud (2) Force.com IDE (2) Governor Limit (2) Groups (2) IBM (2) Implicit Sharing (2) JourneyToCTA (2) Kitchener User Group (2) Lightning Design System (2) Live Agent (2) Metadata (2) PD II (2) Price Book (2) SOSL (2) Sharing (2) Spring 15 (2) Summer17 (2) Territory (2) ant (2) automation tool (2) basic (2) chatter (2) coding (2) communication (2) console (2) controller (2) documentation (2) flow (2) git (2) jquery (2) logging (2) object (2) permission (2) process builder (2) release (2) salesforce1 (2) strategy (2) xml (2) Action Plan (1) Action Plan Template (1) Advanced Currency (1) Agent Productivity (1) Analytics (1) Apex Sharing (1) Arrow (1) Asynchronous Apex (1) Aura Framework (1) Batch (1) Bots (1) Browser (1) Bulk data load (1) CTA (1) Calendar (1) Canon (1) Case Management (1) Celebration (1) Cheat Sheet (1) Classic (1) Community (1) Confetti (1) Constructor (1) Contact Center (1) Continuation (1) Continuous Integration (1) Convert (1) Cookie (1) Custom Metadata (1) Custom Object (1) Customer (1) Dated Exchange Rate (1) Decorator Design Pattern (1) Dev Hub (1) Diwali (1) Email (1) FSC (1) Function (1) Future (1) Goals (1) Guide (1) Household (1) Ideas (1) Improvement (1) KPIs (1) Large Data Volume (1) LastModifiedDate (1) Lightning Web Component (1) Manage Currencies (1) Manual Sharing (1) Metrics (1) Multi Currency (1) New (1) New Feature (1) OOPS (1) OWD (1) Omni-Channel (1) Partner (1) Person Account (1) Photo (1) Pipeline (1) Platform Developer I (1) Platform Developer II (1) Presentation (1) Product Schedule (1) Profile (1) Promise (1) Prototype (1) Public Site (1) Query Plan (1) Queueable (1) QuickReference (1) Reports (1) Retrieve (1) Role Hierarchy (1) SFDX (1) Salesforce Optimizer (1) Schedule (1) Session (1) Sharing Rule (1) Sharing Sets (1) Site (1) Skills (1) Snap-ins (1) Spring 17 (1) Summer14 (1) Summer16 (1) Summer19 (1) Switch (1) SystemModStamp (1) User License (1) Users (1) Webservice (1) Winter'15 (1) Winter'17 (1) access (1) actionFunction (1) actionPoller (1) actionRegion (1) actionSupport (1) agile (1) app (1) approval process (1) aura (1) awesome (1) backup (1) bitbucket (1) book (1) campaign (1) change set (1) code (1) code coverage (1) configuration (1) csv (1) custom button (1) custom settings (1) customization (1) data loader (1) database (1) delegate Admin (1) describe (1) dom (1) dreamforce (1) duplicate (1) dynamic (1) equals (1) error (1) field-level security (1) folder (1) ftp (1) generic (1) gift (1) global describe (1) hashcode (1) import wizard (1) jenkins (1) keynote (1) long running requests (1) monitoring (1) mysql (1) page layout (1) personal (1) power of one (1) record type (1) relationship (1) request (1) review (1) sub-tab (1) tab (1) username (1) visual workflow (1) workflow (1)

Total Subscribers

Total Pageviews