Skip to main content

How to Call Apex Methods Synchronously in Salesforce LWC

In plain words: By default, all communication between Lightning Web Components (LWC) and Apex happens asynchronously—meaning your JavaScript keeps running without waiting for the server to reply. However, there are times when Step 2 of your code strictly requires the data returned from Step 1 before it can proceed. To achieve this "synchronous" behavior in JavaScript, we use modern 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;
        }
    }
}
Developer Trap: Blocking the UI Thread
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.
Step-by-Step Execution Flow:
  1. User clicks the button, triggering handleRunSyncCall().
  2. The UI sets a loading spinner (this.isLoading = true).
  3. JavaScript sends the request to Apex and pauses at the await line.
  4. Apex processes the query and returns the classification string.
  5. JavaScript wakes up, assigns the result to this.classificationResult, and turns off the loading spinner.
360 Card: Asynchronous vs. "Synchronous" LWC Patterns
  • 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.
Core Takeaway: To execute Apex calls sequentially in LWC without messy callback chains, use JavaScript 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!