30

Assume I have an LWC component and Apex Aura controller:

/* AccountController.cls */
public with sharing class AccountController {
    @AuraEnabled
    public static List<Account> getAll() {
        try {
            /* some foo logic */
            return [SELECT Id, Name FROM Account];
        } catch (Exception e) {
            throw new AuraHandledException(e.getMessage());
        }
    }
}
<!--cmp.html --->
<template>
    <template if:false={isLoaded}>
        <lightning-spinner variant="brand" alternative-text="Loading..."></lightning-spinner>
    </template>
</template>
/* cmp.js */
import { LightningElement, track } from 'lwc';
import getAll from '@salesforce/apex/AccountController.getAll';

export default class Cmp extends LightningElement {

    @track isLoaded = false;
    @track accounts;
    @track error;

    connectedCallback() {
        getAll()
            .then(result => {
                console.log('1st then');
                this.accounts = result;
                /* or any other function that returns promise */
                return this.promiseFunc();
            })
            .then(value => {
                console.log('2nd then executes after 3 seconds , value: ' + value);
            })
            .catch(error => {
                this.error = error;
            })
            .finally(() => {
                this.isLoaded = true;
            });
    }

    promiseFunc() {
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve('foo');
            }, 3000);
        });
    }
}

This example works perfectly, but when tried to rewrite promises into async/await:

/* cmp.js */
import { LightningElement, track } from 'lwc';
import getAll from '@salesforce/apex/AccountController.getAll';

export default class Cmp extends LightningElement {

    @track isLoaded = false;
    @track accounts;
    @track error;

    connectedCallback() {
        var result = await getAll();
        this.accounts = result;
        var value = await this.promiseFunc();
        console.log('returned after 3s , value: ' + value);
        this.isLoaded = true;
    }
}

It throws Error

Error:(35, 15) LWC1503: Parsing error: await is a reserved word (35:15)

But if both getAll() and this.promiseFunc() returns promises, why can't I await them? Is async/await syntax is allowed for Lightning Web Components? If yes then how to rewrite promises into async/await correctly and how to catch possible errors?

2 Answers 2

39

async/await syntax is fully supported by LWC. However, as it was introduced in ES8, in old browsers, this syntax is transpiled down to ES5, which can cause performance issues if the code is executed many times.

the await keyword is only valid inside async functions

In your case to make your code deployable with using async/await syntax you can use 3 approaches:

  1. Declare some async init() function and invoke it within connectedCallback() hook:
connectedCallback() {
    this.init();
}


async init() {
    try {
        this.accounts = await getAll();
        const value = await this.promiseFunc();
        console.log('2nd then executes after 3 seconds async, value:' + value);
    } catch (error) {
        this.error = error;
    } finally {
        this.isLoaded = true;
    }
}
  1. Invoke it anonymously as an IIFE:
connectedCallback() {
    (async () => {
        try {
            this.accounts = await getAll();
            const value = await this.promiseFunc();
            console.log('2nd then executes after 3 seconds async, value:' + value);
        } catch (error) {
            this.error = error;
        } finally {
            this.isLoaded = true;
        }
    })();
}
  1. Make your connectedCallback() lifecycle hook async:
async connectedCallback() {
    try {
        this.accounts = await getAll();
        await this.errorPromise();
        const value = await this.promiseFunc();
        console.log('2nd then executes after 3 seconds async, value:' + value);
    } catch (error) {
        this.error = error;
    } finally {
        this.isLoaded = true;
    }
}

I wouldn't recommend you to use the 3rd option at least for API consistency. Not sure about long term effects.

As for catching errors of async functions you can use try/catch block or handle them separately, using catch() method because async function implicitly returns a promise:

this.accounts = await getAll().catch(error => this.error = error);
const value = await this.promiseFunc().catch((/*rejected obj*/) => /*error handling*/);
4
  • Wow it works! Thank you so much!
    – user70363
    Sep 19, 2019 at 16:19
  • 2
    The official documentation uses the third option on the example, so i guess it's API proof
    – Ariel
    Dec 12, 2019 at 14:35
  • Could you please confirm if I can pass a parameter to getAll method?
    – gs650x
    Dec 27, 2019 at 14:22
  • It depends on the apex controller method signature Dec 27, 2019 at 14:24
7

I see that problem is resolved, but I would like to share my thoughts about it. Maybe it will be helpful for someone.

Main assumption:

  • The 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

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');
        }
    }
}

More information here: https://beyondthecloud.dev/blog/promises-in-lwc-lightning-web-components

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .