In the last post, we discussed about Promise function How to chain Asynchronous actions in LWC using Promise? While they provide easy control to Asynchronous JavaScript, but Async/Await which builds on Promises allowing them to be used in a way that much more closely resembles synchronous JavaScript.
Async
It is simply a keyword we put in front of a function definition. Async functions are instances of the AsyncFunction constructor.
async function performAction() { // do some asynchronous action return 'result of an action'; }
An Async function always returns a Promise. That promise will contain the value that you defined.
If you look at the below code script
function start() { console.log('start'); console.log(performAction()); console.log('stop'); } start();
You might expect the below result
start result of an action stop
But in reality the result will be
start stop result of an action
This is because performAction() was executed asynchronously.
Now, to make the above code work like synchronously or to execute all calls in sequential order we need to use Await function.
Await
The await expression causes the async function to pause until a Promise is returned for the async function. So, in simple words we are essentially telling our code to wait for the result of the asynchronous function to complete going any further.
await always expects a Promise from the async function, and parses the result automatically.
Lets consider the above code snippet
async function performAction() { // do some asynchronous action return 'result of an action'; } async function start() { console.log('start'); console.log(await performAction()); console.log('stop'); } start();
Now the result will be
start result of an action stop
This is because we are waiting the function performAction() to be completed before proceeding further.
NOTE: await can only be used within Async function.
Usage in LWC
Problem Statement : I need to re-render the validation message whenever the user checks Another amount as a radio button, but for me the DOM is not getting rendered properly on UI. So, I need to make a wait before I check for the selected radio button and checks the validity and reset it. So, to make that happen I need to put my change action in async/await.
Below is the code snippet:
payment.html
<!-- @description : @author : @tandonprateek @group : @last modified on : 04-15-2022 @last modified by : @tandonprateek --> <template> <div class="slds-grid slds-gutters slds-var-m-bottom_small"> <div class="slds-col slds-size_1-of-3 slds-nowrap"> {labels.component.amountToPay} </div> <div class="slds-col"> <lightning-radio-group name="amountToPay" label={labels.component.amountToPay} variant="label-hidden" options={amountToPayOptions} value={amountToPaySelection} type="radio" onclick={handleAmountToPayChange} onchange={handleAmountToPayChange} class="slds-var-m-bottom_small requiredInput" required> </lightning-radio-group> <lightning-input type="number" name="anotherAmount" label="Another Amount" variant="label-hidden" value={anotherAmount} min="1" onfocus={handleAnotherAmountFocus} onchange={handleAnotherAmountChange} formatter="currency" class="inputAmount anotherAmount requiredInput" required={isAnotherAmountRequired}> </lightning-input> </div> </div> </template>
payment.js
/** * @description : * @author : @tandonprateek * @group : * @last modified on : 05-05-2022 * @last modified by : @tandonprateek **/ import { LightningElement } from 'lwc'; const ANOTHER_AMOUNT_OPTION = 'anotherAmount'; const AMOUNT_DUE_TODAY_OPTION = 'amountDueToday'; export default class Payment extends LightningElement { labels = { toast: { processingErrorTitle: 'Processing Error' , validationErrorTitle: 'Input Validation Error' , validationErrorMessage: 'Please provide values for all required fields' }, component: { paymentHeader: 'Payment' , amountToPay: 'Amount to Pay' , amountDueToday: 'Amount Due Today ' , remainingBalance: 'Remaining Balance ' , anotherAmount: 'Another Amount' } } amountToPaySelection; anotherAmount; isAnotherAmountRequired = false; connectedCallback() { this.amountToPaySelection = AMOUNT_DUE_TODAY_OPTION } /** This method will wait for the Promise to be resolved and will then reset the validity. **/ async handleAmountToPayChange(event) { this.amountToPaySelection = event.detail.value; const anotherAmountField = this.template.querySelector('.anotherAmount'); if(this.amountToPaySelection !== ANOTHER_AMOUNT_OPTION) { this.anotherAmount = undefined; await Promise.resolve(); anotherAmountField.reportValidity(); } else { anotherAmountField.focus(); anotherAmountField.reportValidity(); } this.isAnotherAmountRequired = this.amountToPaySelection === ANOTHER_AMOUNT_OPTION; } handleAnotherAmountChange(event) { const value = event.detail.value; this.anotherAmount = value; } handleAnotherAmountFocus(event) { this.amountToPaySelection = ANOTHER_AMOUNT_OPTION; this.isAnotherAmountRequired = this.amountToPaySelection === ANOTHER_AMOUNT_OPTION; } get amountToPayOptions() { const theOptions = []; theOptions.push({ label: this.labels.component.amountDueToday, value: AMOUNT_DUE_TODAY_OPTION}); let balanceLabel = this.labels.component.remainingBalance; theOptions.push({ label: balanceLabel, value: 'remainingBalance'}); theOptions.push({ label: this.labels.component.anotherAmount, value: ANOTHER_AMOUNT_OPTION }); return theOptions; } }
Error Handling
To handle error we will be using old try/catch block.
async function doSomething() { // do something asyncronous return 'something'; } async function start() { try { console.log('start'); console.log(await performAction()); console.log('end'); } catch(e) { console.error(e); } finally { console.log('Do something no matter what'); } } start();