Async/Await Concept in Javascript/LWC

 

Concept of async and await in JavaScript

async and await are used in asynchronous programming in JavaScript. They help us write cleaner, more readable, and more manageable asynchronous code without using complex callback functions or Promise chains.


🔸 Why Do We Need async/await?

JavaScript is single-threaded, meaning it executes one operation at a time. However, sometimes we need to perform long-running tasks like:

  • Fetching data from a server (API calls)
  • Reading files
  • Accessing databases
  • Waiting for user input

If JavaScript waits for each task to complete before moving to the next, the app becomes slow and unresponsive. To avoid this, we use asynchronous programming.


🔹 async Function

An async function always returns a Promise. This means the function runs in the background and does not block the execution of other code.

🔹 Example:

javascript

async function fetchData() { return "Hello, World!"; } fetchData().then(response => console.log(response)); // Output: Hello, World!

Here, fetchData() returns a Promise that resolves to "Hello, World!".


🔹 await Keyword

The await keyword pauses the execution of an async function until a Promise is resolved.

🔹 Example:

javascript

async function fetchData() { let response = await fetch('https://jsonplaceholder.typicode.com/todos/1'); let data = await response.json(); console.log(data); } fetchData();

What Happens Here?

  1. fetch() returns a Promise, which is awaited.
  2. await response.json() waits for the JSON data.
  3. console.log(data) runs only after the data is available.

🚀 Without await, JavaScript would move to the next line before fetch() completes, leading to undefined or incomplete data.


🔸 Key Benefits of async/await

Code looks synchronous & easy to read
Avoids callback hell (nested callbacks)
Better error handling with try...catch


🔹 Handling Errors in async/await

We can use try...catch to handle errors in an async function.

🔹 Example:

javascript

async function fetchData() { try { let response = await fetch('https://invalid-url.com'); let data = await response.json(); console.log(data); } catch (error) { console.error('Error fetching data:', error); } } fetchData();

If an error occurs, catch will handle it gracefully instead of crashing the program.


🔸 Comparison: async/await vs Promises

FeaturePromises (.then/.catch)async/await
SyntaxCallback-based chainingLooks like synchronous code
ReadabilityHarder (nested .then())Easier, cleaner
Error Handling.catch() neededtry...catch is simpler
PerformanceSimilarSimilar

🔹 Example with Promises (.then/.catch)

javascript

fetch('https://jsonplaceholder.typicode.com/todos/1') .then(response => response.json()) .then(data => console.log(data)) .catch(error => console.error('Error:', error));

🔹 Same Example with async/await

javascript

async function fetchData() { try { let response = await fetch('https://jsonplaceholder.typicode.com/todos/1'); let data = await response.json(); console.log(data); } catch (error) { console.error('Error:', error); } } fetchData();

🎯 Both do the same thing, but async/await is cleaner and more readable!


🔹 When to Use async/await?

✅ When dealing with APIs, databases, or file reading
✅ When you need sequential execution of async tasks
✅ When you want to handle errors easily with try...catch


🔥 Final Thought

async/await makes JavaScript asynchronous code more readable, maintainable, and easier to debug. It's a powerful tool for working with Promises in a cleaner way.


Best Practices for Apex Trigger in Salesforce

 

1. Follow the One Trigger Per Object Principle

  • Create a single trigger per object to centralize logic and avoid conflicts.
  • Use a handler class to process the logic, separating the trigger's definition from its implementation.

2. Use Context Variables

  • Use context variables like Trigger.isInsert, Trigger.isUpdate, Trigger.isDelete, etc., to identify the context of the trigger execution.
  • This makes your code modular and avoids redundancy.

3. Bulkify Your Code

  • Always assume that triggers will handle multiple records (bulk operations).
  • Use collections like List, Set, or Map instead of working with a single record.

Example:

apex

List<Account> accountsToUpdate = new List<Account>(); for (Account acc : Trigger.new) { if (acc.Industry == 'Technology') { acc.Rating = 'Hot'; accountsToUpdate.add(acc); } } update accountsToUpdate;

4. Avoid SOQL and DML in Loops

  • Perform SOQL (Salesforce Object Query Language) queries and DML (Data Manipulation Language) operations outside loops to avoid hitting governor limits.

Bad Practice:

apex

for (Contact con : Trigger.new) { Account acc = [SELECT Name FROM Account WHERE Id = :con.AccountId]; acc.Description = 'Updated by Trigger'; update acc; }

Good Practice:

apex

Set<Id> accountIds = new Set<Id>(); for (Contact con : Trigger.new) { accountIds.add(con.AccountId); } Map<Id, Account> accountMap = new Map<Id, Account>( [SELECT Id, Name FROM Account WHERE Id IN :accountIds] ); List<Account> accountsToUpdate = new List<Account>(); for (Contact con : Trigger.new) { if (accountMap.containsKey(con.AccountId)) { Account acc = accountMap.get(con.AccountId); acc.Description = 'Updated by Trigger'; accountsToUpdate.add(acc); } } update accountsToUpdate;

5. Handle Exceptions Gracefully

  • Use try-catch blocks to handle exceptions and log errors using custom objects or the System.debug() method.

6. Ensure Trigger Recursion is Controlled

  • Prevent recursive trigger execution by using static variables.

Example:

apex

public class TriggerHandler { public static Boolean isExecuting = false; }

In Trigger:

apex

if (!TriggerHandler.isExecuting) { TriggerHandler.isExecuting = true; // Trigger logic here TriggerHandler.isExecuting = false; }

7. Write Comprehensive Test Classes

  • Achieve 100% code coverage by testing all possible scenarios, including bulk, single, positive, and negative cases.
  • Use Test.startTest() and Test.stopTest() to validate limits and asynchronous behavior.

8. Minimize Hard-Coding

  • Use Custom Settings, Custom Metadata, or Label records instead of hard-coded values.

9. Follow Naming Conventions

  • Use clear and descriptive names for triggers and handler methods (e.g., AccountTrigger and AccountTriggerHandler).

10. Maintain a Trigger Framework

  • Implement a trigger framework to standardize your approach. Popular frameworks include the TDTM (Trigger Handler Framework) and SObject Framework.

Example Structure:

apex

trigger AccountTrigger on Account (before insert, before update) { AccountTriggerHandler handler = new AccountTriggerHandler(); if (Trigger.isBefore) { if (Trigger.isInsert) { handler.beforeInsert(Trigger.new); } else if (Trigger.isUpdate) { handler.beforeUpdate(Trigger.new); } } }

Handler Class Example:

apex

public class AccountTriggerHandler { public void beforeInsert(List<Account> newRecords) { // Logic here } public void beforeUpdate(List<Account> newRecords) { // Logic here } }

11. Test and Optimize Performance

  • Use Salesforce's debug logs to monitor trigger execution and optimize queries, loops, and DML operations for better performance.

Getter and Setter in Salesforce Lightning Web Component (LWC )

 Let’s have a sample LWC component without getter and setter.

 

javascript

import { LightningElement } from 'lwc';

 export default class NoGetterSetterExample extends LightningElement {

    name = 'John'; // Direct assignment

}

 

Html

<template>

    <p>The name is: {name}</p>

</template>

 

 

Output:

The name is: John


 Here we can directly set the value of name to 'John' and {name} will display value ‘John’.

 

Now we see similar example using getter and setter:

 

javascript:

import { LightningElement } from 'lwc';

 export default class GetterSetterExample extends LightningElement {

    _name = ''; // Private property for internal use

 

    // Setter for name

    set name(value) {

        this._name = value.toUpperCase(); // Modify value before assigning it

    }

 

    // Getter for name

    get name() {

        return this._name; // Return modified value

    }

 

    connectedCallback() {

        this.name = 'John'; // Trigger setter

    }

}

 

HTML

<template>

    <p>The name is: {name}</p>

</template>

 

 

Result:

The name is: JOHN

 

Here, the getter and setter add extra control:

 

·         The setter allows you to manipulate the input (e.g., convert the name to uppercase).

·         The getter retrieves the manipulated value when the template requests it.

 

Advantages of Using Getters and Setters

Encapsulation: You can hide the internal details of how data is stored or modified and expose only what is necessary.

Data Transformation: With setters, you can format or validate input data before saving it. With getters, you can format the output or return derived properties.

Reactive Behavior: By using setters, you can trigger additional actions (like updating other properties or firing events) whenever a property is set.

 

Example of a Scenario Without Getters and Setters

In a simpler case where no transformations or logic are needed, you can directly bind properties in the template without getters and setters, as shown in the first example. This might be preferred when you just want to display or store data without any additional processing.

 

Conclusion

While you can achieve a similar result without getters and setters, using them provides more control and flexibility, especially when you need to transform data or implement custom logic.

What is Async and Await in Salesforce LWC

In Lightning Web Components (LWC), async and await are modern JavaScript features used to handle asynchronous operations more cleanly and intuitively compared to traditional promise chaining using .then() and .catch() methods. They allow you to write asynchronous code that looks and behaves more like synchronous code, making it easier to read and maintain.

Key Points about async and await in LWC:

  1. async Function: An async function is a function that implicitly returns a promise and allows the use of await within it. Any value returned by an async function is automatically wrapped in a promise.
  2. await Keyword: The await keyword can be used inside an async function to pause the execution of the function until the promise is resolved or rejected. It can only be used inside async functions.

Example Usage:

Here's a step-by-step guide to using async and await in an LWC to call an Apex method imperatively:

  1. Define the Apex Method:
    • Create an Apex class with a method that you want to call. Ensure it is annotated with @AuraEnabled and static.
java

public class MyApexClass { @AuraEnabled public static String getGreeting(String name) { return 'Hello, ' + name; } }
  1. Import the Apex Method in JavaScript:
    • In your LWC JavaScript file, import the Apex method using the @salesforce/apex module.
javascript

import { LightningElement } from 'lwc'; import getGreeting from '@salesforce/apex/MyApexClass.getGreeting';
  1. Use async and await to Call the Apex Method:
    • Define an async function and use await to call the imported Apex method. Handle any potential errors using try and catch.
javascript

export default class MyComponent extends LightningElement { name = 'World'; greeting; async handleButtonClick() { try { this.greeting = await getGreeting({ name: this.name }); } catch (error) { console.error('Error:', error); } } }
  1. Create the HTML Template:
    • Define the HTML template to include elements that trigger the Apex call and display the result.
html

<template> <lightning-button label="Get Greeting" onclick={handleButtonClick}></lightning-button> <p>{greeting}</p> </template>

Full Code Example:

Apex Class (MyApexClass.cls):

java

public class MyApexClass { @AuraEnabled public static String getGreeting(String name) { return 'Hello, ' + name; } }

JavaScript File (myComponent.js):

javascript

import { LightningElement } from 'lwc'; import getGreeting from '@salesforce/apex/MyApexClass.getGreeting'; export default class MyComponent extends LightningElement { name = 'World'; greeting; async handleButtonClick() { try { this.greeting = await getGreeting({ name: this.name }); } catch (error) { console.error('Error:', error); } } }

HTML Template (myComponent.html):

html
<template> <lightning-button label="Get Greeting" onclick={handleButtonClick}></lightning-button> <p>{greeting}</p> </template>

Benefits of Using async and await:

  1. Simplicity: The code looks synchronous, which makes it easier to understand.
  2. Readability: Avoids deeply nested .then() and .catch() chains, improving readability.
  3. Error Handling: Using try and catch provides a clear and concise way to handle errors.

By incorporating async and await in your LWC, you can manage asynchronous Apex method calls more effectively, leading to cleaner and more maintainable code.

Imperative apex method in Salesforce LWC

In Salesforce Lightning Web Components (LWC), an imperative Apex method refers to the way of invoking Apex methods directly from JavaScript code, as opposed to using the declarative @wire service. Imperative calls provide more control over the timing and handling of the method invocation and are useful when you need to call an Apex method in response to user actions, such as button clicks or form submissions.

Key Points about Imperative Apex Methods in LWC:

  1. Explicit Invocation: Imperative calls are made explicitly within your JavaScript code using promises, giving you full control over when and how the method is called.
  2. Error Handling: You can handle success and error responses using .then() and .catch() methods.
  3. Flexibility: This approach is ideal for scenarios where you need to call Apex methods conditionally or in response to specific events that do not fit well with the reactive nature of @wire.

Example:

Here's a step-by-step guide to implementing an imperative Apex method call in a Lightning Web Component:

  1. Define the Apex Method:
    • Create an Apex class with a method that you want to call. Make sure it is annotated with @AuraEnabled and static.

public class MyApexClass { @AuraEnabled public static String getGreeting(String name) { return 'Hello, ' + name; } }
  1. Import the Apex Method in JavaScript:
    • In your LWC JavaScript file, import the Apex method using @salesforce/apex module.
javascript

import { LightningElement } from 'lwc'; import getGreeting from '@salesforce/apex/MyApexClass.getGreeting';
  1. Call the Apex Method Imperatively:
    • Use the imported Apex method within your JavaScript code, typically in response to a user action like a button click.
javascript

export default class MyComponent extends LightningElement { name = 'World'; greeting; handleButtonClick() { getGreeting({ name: this.name }) .then(result => { this.greeting = result; }) .catch(error => { console.error('Error:', error); }); } }
  1. Create the HTML Template:
    • Define the HTML template to include elements that trigger the Apex call and display the result.
html

<template> <lightning-button label="Get Greeting" onclick={handleButtonClick}></lightning-button> <p>{greeting}</p> </template>

Full Code Example:

Apex Class (MyApexClass.cls):

java

public class MyApexClass { @AuraEnabled public static String getGreeting(String name) { return 'Hello, ' + name; } }

JavaScript File (myComponent.js):

javascript

import { LightningElement } from 'lwc'; import getGreeting from '@salesforce/apex/MyApexClass.getGreeting'; export default class MyComponent extends LightningElement { name = 'World'; greeting; handleButtonClick() { getGreeting({ name: this.name }) .then(result => { this.greeting = result; }) .catch(error => { console.error('Error:', error); }); } }

HTML Template (myComponent.html):

html
<template> <lightning-button label="Get Greeting" onclick={handleButtonClick}></lightning-button> <p>{greeting}</p> </template>

By following these steps, you can successfully invoke Apex methods imperatively from your Lightning Web Components, providing a more flexible and controlled approach to server-side processing in your Salesforce applications.

Performance impact of using @track decorator in Salesforce LWC

 The @track decorator in Lightning Web Components (LWC) is used to make properties reactive. This means that any change to a tracked property automatically updates the component's template, causing the relevant part of the DOM to re-render. While this feature is very powerful and essential for building dynamic and responsive user interfaces, it is important to use it judiciously to avoid potential performance issues.

Performance Impacts of Using @track

  1. Frequent Re-renders:

    • Impact: Every change to a tracked property triggers a re-render of the component. If the tracked property is updated frequently, it can lead to performance bottlenecks, especially if the DOM updates are extensive or complex.
    • Mitigation: Ensure that properties are only tracked when necessary. Avoid frequent and unnecessary updates to tracked properties.
  2. Unnecessary Tracking:

    • Impact: Marking too many properties with @track can cause the framework to monitor a large number of changes, leading to increased memory usage and slower performance.
    • Mitigation: Track only those properties that need to trigger UI updates. For simple data that does not affect the UI, do not use @track.
  3. Complex Data Structures:

    • Impact: Tracking complex data structures (like large objects or arrays) can be inefficient because any change to any part of the structure can trigger a re-render.
    • Mitigation: For complex data structures, consider using getters to derive values that need to be reactive. This way, only the specific parts of the structure that need to be reactive are monitored.

Best Practices for Using @track

  1. Track Only What’s Necessary:

    • Use @track sparingly. Only track properties that are directly bound to the template and need to trigger updates.
    javascript

    import { LightningElement, track } from 'lwc'; export default class MyComponent extends LightningElement { @track firstName = 'John'; @track lastName = 'Doe'; }
  2. Use Getters for Derived State:

    • Instead of tracking derived properties, use getters. Getters are recalculated only when their dependencies change, thus avoiding unnecessary tracking and updates.
    javascript

    import { LightningElement, track } from 'lwc'; export default class MyComponent extends LightningElement { @track firstName = 'John'; @track lastName = 'Doe'; get fullName() { return `${this.firstName} ${this.lastName}`; } }
  3. Avoid Tracking Large Structures:

    • If you need to track large or complex data structures, break them down into smaller, trackable parts. Alternatively, update only the necessary parts and use methods to handle updates more efficiently.
    javascript

    import { LightningElement, track } from 'lwc'; export default class MyComponent extends LightningElement { @track person = { firstName: 'John', lastName: 'Doe' }; updateFirstName(newName) { this.person = { ...this.person, firstName: newName }; } }
  4. Consider Alternative Strategies:

    • Sometimes, leveraging other state management techniques, such as custom events or external state libraries, can help manage state changes more efficiently.
    javascript

    import { LightningElement, track } from 'lwc'; export default class ParentComponent extends LightningElement { @track message = ''; handleChildEvent(event) { this.message = event.detail.message; } }
    html
    <!-- parentComponent.html --> <template> <child-component onmessagechange={handleChildEvent}></child-component> <p>{message}</p> </template>

Conclusion

The @track decorator is a powerful tool for ensuring reactivity in Lightning Web Components, but it should be used thoughtfully to avoid performance pitfalls. By following best practices—tracking only necessary properties, using getters for derived state, avoiding large structures, and considering alternative state management techniques—you can maintain optimal performance in your LWC applications.

Achieve different sharing modes (with sharing and without sharing) for authenticated and unauthenticated users when calling an Apex method

 To achieve different sharing modes (with sharing and without sharing) for authenticated and unauthenticated users when calling an Apex method, you can leverage a combination of Apex classes and conditional logic to determine the user's authentication status.

Steps to Achieve This:

  1. Create Two Apex Classes:

    • One with with sharing
    • One with without sharing
  2. Use a Wrapper Class:

    • Create a wrapper class or a method that checks if the user is authenticated or not and calls the appropriate class.

Example Implementation:

Step 1: Create Two Apex Classes

Class with with sharing:

apex

public with sharing class SharedService { @AuraEnabled public static void performAction() { // Your logic here } }

Class with without sharing:

apex

public without sharing class UnsharedService { @AuraEnabled public static void performAction() { // Your logic here } }

Step 2: Wrapper Class to Determine User Type

Wrapper Class:

apex

public class ServiceWrapper { @AuraEnabled public static void performAction() { if (isAuthenticatedUser()) { SharedService.performAction(); } else { UnsharedService.performAction(); } } private static Boolean isAuthenticatedUser() { // Check the user authentication status // You can use UserInfo class to check if the user is authenticated return UserInfo.getUserType() != 'Guest'; } }

Explanation:

  1. Separate Classes:

    • SharedService and UnsharedService are the two Apex classes. One operates in the with sharing mode, and the other operates in the without sharing mode.
  2. Wrapper Class:

    • ServiceWrapper class contains the performAction method that checks if the current user is authenticated using UserInfo.getUserType().
    • If the user is authenticated (UserInfo.getUserType() != 'Guest'), it calls the method in the SharedService class.
    • If the user is unauthenticated, it calls the method in the UnsharedService class.
  3. User Authentication Check:

    • UserInfo.getUserType() returns the type of user. For guest users, it returns Guest.

Calling the Wrapper Class from LWC:

When you call the Apex method from your Lightning Web Component (LWC), you should call the method in the ServiceWrapper class:

javascript

import { LightningElement, track, wire } from 'lwc'; import performAction from '@salesforce/apex/ServiceWrapper.performAction'; export default class MyComponent extends LightningElement { handleAction() { performAction() .then(result => { // Handle success }) .catch(error => { // Handle error }); } }

By using this approach, you ensure that authenticated users are subjected to sharing rules, while unauthenticated users bypass them, achieving the desired behavior based on user authentication status.

Async/Await Concept in Javascript/LWC

  Concept of async and await in JavaScript async and await are used in asynchronous programming in JavaScript. They help us write clean...