Promises in LWC (Lightning Web Components)

Hello folks!
Today I would like to give you a short story of JavaScript Promise and show you how it is used in Salesforce Lightning Web Components.

Let’s get started!

Promise

Promise lets asynchronous methods return values like synchronous methods: instead of immediately returning the final value, the asynchronous method returns a promise to supply the value at some point in the future.” ~ MDN

A promise can have one of the following states

  • pending – initial state, an action waiting to be a fire
  • fulfilled – operation completed successfully
  • rejected – operation failed

We can use a few JavaScript methods to chained promises

  • promise.then()
  • promise.catch()
  • promise.finally()

IF promise has state fulfilled THEN then the method is fire.
ELSE IF promise has state rejected THEN catch method is fire.
finally is fire always at the end of the promise. Helps to avoid duplication code in then and catch

const asynchronousMethod = (resolve, reject) => {
   setTimeout(() => {
      resolve('Test'); // or reject('TestError')l
   }, 500);
}

// resolve funtion call on fulfilled
// reject funtion call on rejected 

new Promise(asynchronousMethod)
.then((value) => {
   // If the promise resolves, we enter this code block
   console.log(value); // Test
})
.catch((error) => {
   // If the promise rejects, we enter this code block
   console.log(error); // TestError
})
.finally(() => {
   console.log('Finally'); // Finally
})

// output: Test Finally

Promises in LWC

Promises in Lightning Web Components are usually used to invoke apex methods.
The imported function returns a promise. ” ~ Salesforce

I prepared a simple APEX Contact Controller

public class ContactController {
   
    @AuraEnabled
    public static Contact getContactDetails(Id recordId){
        return [ SELECT Id, Name 
                 FROM Contact
                 WHERE Id = :recordId ];
    }

    @AuraEnabled
    public static string getTextMethod1(){
        return 'APEX-METHOD-1';
    }

    @AuraEnabled
    public static string getTextMethod1(){
        return 'APEX-METHOD-2';
    }

    @AuraEnabled
    public static string getTextMethod3(){
        return 'APEX-METHOD-3';
    }
}

Below you can find LWC code which handles apex function. Please note that we have then, catch, finally methods.

import { LightningElement } from 'lwc';
import getContactDetails from '@salesforce/apex/ContactController.getContactDetails';

export default class LwcPromise extends LightningElement {

    connectedCallback() {
        this.getContactDetailsApex('0033V00000AQ3ptQAD');
    }

    getContactDetailsApex(contactId) {
        getContactDetails({
            recordId: contactId 
        })
        .then((result) => {
            console.log(result);
        })
        .catch((error) => {
            console.log(error);
        })
        .finally(() => {
            console.log('Finally');
        })
        /* OUTPUT
           {Id: "0033V00000AQ3ptQAD", Name: "Emanuel Manzanares"}
           Finally
        */
    }
}

Aggregate Promises in LWC

Assume that you need to invoke the APEX method in the loop and you don’t have bulk apex method. The best approach for it will be to use Promise.all()

import { LightningElement } from 'lwc';
import getContactDetails from '@salesforce/apex/ContactController.getContactDetails';

const CONTACTS_IDS = [
    '0033V00000AQ3ptQAD',
    '0033V00000AQ3pzQAD',
    '0033V00000AQ3pvQAD'
 ];

export default class LwcPromise extends LightningElement {

    connectedCallback() {
        const apexPromises = CONTACTS_IDS.map(contactId => getContactDetails({
            recordId: contactId
        }));
        this.resolveApexPromises(apexPromises);
    }

    resolveApexPromises(apexPromises) {
        Promise.all(apexPromises)
        .then((result) => {
            console.log(result);
        })
        .catch((error) => {
            console.log(error);
        })
        .finally(() => {
            console.log('Finally');
        })
        /* OUTPUT
           0: {Id: "0033V00000AQ3ptQAD", Name: "Emanuel Manzanares"}
           1: {Id: "0033V00000AQ3pzQAD", Name: "Millie Brooks"}
           2: {Id: "0033V00000AQ3pvQAD", Name: "Takuya Watanabe"}
           Finally
         */
    }
}

Chain Promises in LWC

Methods are independent but need some order

Operation A – Operation B – Operation C

import { LightningElement } from 'lwc';
import getContactDetails from '@salesforce/apex/ContactController.getContactDetails';

export default class LwcPromise extends LightningElement {

    connectedCallback() {
        this.getContactDetailsApex('0033V00000AQ3ptQAD');
    }

    getContactDetailsApex(contactId) {
        getContactDetails({
            recordId: contactId
        })
        .then((result) => {
            console.log('Operation A' + result);
        })
        .then((result) => {
            console.log('Operation B');
        })
        .then((result) => {
            console.log('Operation C');
        })
        .catch((error) => {
            console.log(error);
        })
        .finally(() => {
            console.log('Finally');
        })
    }

    /* Output
        Operation A
        Operation B
        Operation C
        Finally
    */

}

Methods are dependent (Promises example)

In the following example, the code looks like a mess. A lot of .then methods, which not give us a clear view of code logic.

import { LightningElement } from 'lwc';

import getTextMethod1 from '@salesforce/apex/ContactController.getTextMethod1';
import getTextMethod2 from '@salesforce/apex/ContactController.getTextMethod2';
import getTextMethod3 from '@salesforce/apex/ContactController.getTextMethod3';

export default class LwcPromise extends LightningElement {

    connectedCallback() {
        this.invokeApexMethods();
    }

    invokeApexMethods() {
        getTextMethod1()
        .then((result1) => {
            console.log('Method1 result: ' + result1);

            getTextMethod2({
                message1: result1
            })
            .then((result2) => {
                console.log('Method2 result: ' + result2);

                getTextMethod3({
                    message2: result2
                })
                .then((result3) => {
                    console.log('Method3 result: ' + result3);
                })
                .catch((error => {
                    console.log(error);
                }));
            })
            .catch((error => {
                console.log(error);
            }));
        })
        .catch((error) => {
            console.log(error);
        })
        .finally(() => {
            console.log('Finally Block');
        })
    }
}
Output
Method1 result: APEX-METHOD-1
Finally Block
Method2 result: APEX-METHOD-1 APEX-METHOD-2
Method3 result: APEX-METHOD-1 APEX-METHOD-2 APEX-METHOD-3

Methods are dependent (async/await example)

Async/await allows us to have a simple and what’s important clear code!

import { LightningElement } from 'lwc';

import getTextMethod1 from '@salesforce/apex/ContactController.getTextMethod1';
import getTextMethod2 from '@salesforce/apex/ContactController.getTextMethod2';
import getTextMethod3 from '@salesforce/apex/ContactController.getTextMethod3';

export default class LwcPromise extends LightningElement {

    connectedCallback() {
        this.invokeApexMethods();
    }

    async invokeApexMethods() {
        try {
            const result1 = await getTextMethod1();
            console.log('Method1 result: ' + result1);
            const result2 = await getTextMethod2({
                message1: result1
            });
            console.log('Method2 result: ' + result2);
            const result3 = await getTextMethod3({
                message2: result2
            });
            console.log('Method3 result: ' + result3);
        } catch(error) {
            console.log(error);
        } finally {
            console.log('Finally Block');
        }
    }
}
Output
Method1 result: APEX-METHOD-1
Method2 result: APEX-METHOD-1 APEX-METHOD-2
Method3 result: APEX-METHOD-1 APEX-METHOD-2 APEX-METHOD-3
Finally Block
  • Async function returns a promise
  • Await is used for calling an async function and waits for resolve/reject
  • Use await if operation B is dependent on operation A
  • Async/await is an excellent option if you find yourself writing long, complicated waterfalls of .then statements

Was it helpful? Check out our other great posts here.

Resources

  1. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/Promise
  2. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
  3. https://levelup.gitconnected.com/async-await-vs-promises-4fe98d11038f
4.7 18 votes
Article Rating
Subscribe
Notify of
guest
5 Comments
Newest
Oldest Most Voted
Inline Feedbacks
View all comments
Ana Bell
Ana Bell
1 year ago

great! thanks!

dim
dim
1 year ago

Yess! Great post! Many thanks Piotr

Ola
Ola
1 year ago

Great post!

Close Menu
5
0
Would love your thoughts, please comment.x
()
x