async/await and JavaScript Promises.
In Salesforce Lightning Web Components, invoking server-side Apex methods is traditionally handled via promises or wire adapters. Because web browsers are designed not to freeze while waiting for a server response, everything happens asynchronously.
Sometimes, business logic dictates strict sequential execution—you need to call an Apex method, wait for the response, and only then execute the next line of frontend code. Let's explore how to handle this cleanly using modern JavaScript async/await patterns.
1. The Apex Controller
First, let's write a standard Apex controller method decorated with @AuraEnabled so it can be exposed to our Lightning component.
public with sharing class SyncApexCallController {
@AuraEnabled(cacheable=true)
public static String getAccountClassification(String accountId) {
// Perform server-side processing
Account acc = [SELECT Id, AnnualRevenue FROM Account WHERE Id = :accountId LIMIT 1];
if (acc.AnnualRevenue > 1000000) {
return 'Enterprise Client';
} else {
return 'Standard Client';
}
}
}
2. Invoking Apex Synchronously in LWC Using Async/Await
Instead of relying on complex event messaging services, you can import your Apex method directly and wrap your call in an async function. This allows you to use the await keyword, pausing function execution until Salesforce returns the server response.
import { LightningElement, track } from 'lwc';
import getAccountClassification from '@salesforce/apex/SyncApexCallController.getAccountClassification';
export default class SyncApexCallDemo extends LightningElement {
@track classificationResult = '';
@track isLoading = false;
// Triggered by a button click in the HTML template
async handleRunSyncCall() {
this.isLoading = true;
try {
// The 'await' keyword pauses execution here until Apex replies!
const result = await getAccountClassification({ accountId: '001xx000003DGb2AAG' });
this.classificationResult = result;
console.log('Successfully received result synchronously:', result);
} catch (error) {
console.error('Error executing Apex call:', error);
} finally {
this.isLoading = false;
}
}
}
While
async/await makes your code look synchronous and sequential, it is still non-blocking under the hood. Never attempt to write infinite loops or blocking synchronous HTTP requests on the client browser. Always let JavaScript handle network calls asynchronously via Promises.
- User clicks the button, triggering
handleRunSyncCall(). - The UI sets a loading spinner (
this.isLoading = true). - JavaScript sends the request to Apex and pauses at the
awaitline. - Apex processes the query and returns the classification string.
- JavaScript wakes up, assigns the result to
this.classificationResult, and turns off the loading spinner.
- Traditional Promise (
.then().catch()): Callback hell can make sequential steps hard to read. - Async/Await: Modern ES8 syntax that lets you write asynchronous network calls that read and behave like clean, sequential code.
async/await syntax to pause execution cleanly until the server responds.
Conclusion
Handling sequential operations in Lightning Web Components no longer requires complex Lightning Message Service workarounds. By leveraging native JavaScript async/await patterns, you can execute server-side Apex calls in a clear, predictable order while maintaining a smooth, responsive user interface.
Happy coding!