0

I have a method in which I am making an imperative apex call which returns the result in the form of a Map<String, Boolean>, this again I am converting to a map (I need it in map form as in iteration below I will use map.get) in the client side since the map is coming as an object from Server side. Problem is when I'm converting the map within this block it shows in the console, but when I am accessing this map in an iteration in another block below (in the same method) it is undefined as per console log, so my logic is failing. Here is the code

import getFavAccountBoolean from '@salesforce/apex/ECX_AccountPickerHelper.getFavAccountBoolean';
import { LightningElement, wire, track } from 'lwc';
import { loadStyle } from "lightning/platformResourceLoader";
import { NavigationMixin } from 'lightning/navigation';

 export default class EcxAccountPicker extends NavigationMixin(LightningElement) {

    mapNew ;


// other code

 objectToMap(obj){

        return new Map(Object.entries(obj));
    }


  convertToObject(returnValue){
        let arr=[];
        var map1 = new Map();
            
        this.accountSet = returnValue;
        console.log('all accounts are', this.accountSet);

        getFavAccountBoolean({emaAccounts: this.accountSet}).then(result =>{

         map1 = this.objectToMap(result);

        console.log('map is' ,map1);  // this console showing correct output as expected
       
        }).catch(error =>{

            console.log(error);

        })  
    
        this.mapNew = new Map(map1);
        console.log('checking if map value copied', this.mapNew);  // shows undefined

        this.accountSet.forEach((item,index) =>{
            if(item != null){
                let obj ={};
                obj.id = item.TargetId;
                obj.accountName = item.Target.Name;
                obj.address = item.Target.ShippingCity + ', '+ item.Target.ShippingState;
                obj.accountNumber = item.Target.EC_Account_Number__c;
                obj.tag = item.Target.EC_Division__c;
              
       console.log('map is', map1);   // undefined
       console.log('fav value for this particular account', map1.get(item.TargetId))  //undefined
                obj.isFav = map1.get(item.TargetId); // undefined
                
       }     

The Apex method works fine as I checked applying debug logs,it returns an object of account Id and boolean value. I initially was using let map1 = new Map() then I realized the difference between let and var so I used var for function scope as well, problem is outside the block of imperative server call wherever I am accessing the map value it shows undefined. I even tried setting it at a global LWC class level variable as above still it shows undefined. What can be the possible reason that I am not able to access this map1 outside the imperative apex call block?

1 Answer 1

2

An imperative Apex call runs asynchronously. Consider the following code:

function doAsync() {
  return new Promise((resolve) => setTimeout(resolve,100));
}
console.log('I am first');
doAsync().then(() => console.log('I am third'));
console.log('I am second');

You'll see the following output:

I am first
I am second
(( note: 1/10th of a second delay here ))
I am third

The reason why you don't see any output here:

console.log("checking if map value copied", this.mapNew); // shows undefined

Is the same reason why I am second came before I am third; the asynchronous code has not had a chance to run yet. Either place all of your logic into the .then callback, or use async and await:

  // async is required to use await in a function
  async convertToObject(returnValue) {
    let arr = [];
    var map1 = new Map();

    this.accountSet = returnValue;
    console.log("all accounts are", this.accountSet);

    // Your code will pause here until the results are available
    let result = await getFavAccountBoolean({ emaAccounts: this.accountSet })
    map1 = this.objectToMap(result);

    console.log("map is", map1); // this console showing correct output as expected

    this.mapNew = new Map(map1);
    console.log("checking if map value copied", this.mapNew); // shows undefined

    this.accountSet.forEach((item, index) => {
      if (item != null) {
        let obj = {};
        obj.id = item.TargetId;
        obj.accountName = item.Target.Name;
        obj.address =
          item.Target.ShippingCity + ", " + item.Target.ShippingState;
        obj.accountNumber = item.Target.EC_Account_Number__c;
        obj.tag = item.Target.EC_Division__c;

        console.log("map is", map1); // undefined
        console.log(
          "fav value for this particular account",
          map1.get(item.TargetId)
        ); //undefined
        obj.isFav = map1.get(item.TargetId); // undefined
      }
    });
  }
5
  • thanks but if I make the convertToObject method as async it wont affect or alter the execution of the other flows of the remaining method correct? Placing this keyword will mean it will only wait for the async method to execute first and return a response., nothing more right? Moreover I thought imperative is synchronous as in documentation I saw its normally used in a context such as a button click, or execution flow from a connectedCallBack etc.
    – Amax1
    Jul 19, 2023 at 5:17
  • This part which you suggested works, but Im facing an issue after making this method as async, this convertToObject method pushes these objects (obj) in an array and returns this array as a resultSet to another method, that method takes this resultSet as a parameter and iterates over it using forEach and is not an async method. So at the resultSet.forEach in that method, the console says resultSet.forEach is not a function
    – Amax1
    Jul 19, 2023 at 5:51
  • @Amax1 The documentation may not make it clear, but server calls are asynchronous. Nobody anywhere on the modern web are using synchronous callouts, because they freeze the UI thread entirely when you do this. In fact, browsers will emit a warning/error if you try. The function calling this one also needs to await, or use then, in order to get the proper results back.
    – sfdcfox
    Jul 19, 2023 at 15:36
  • @sfdxfox but as a thumb rule when do we need to apply async await while making imperative apex calls? Because this is the first time I faced this scenario. There have been instances (such as button click) or calling a this.method( ) within a connectedCallBack and inside it making an imperative apex call to fetch data while component loading, in these cases I have never had to make async await in the methods that call the imperative apex methods
    – Amax1
    Jul 19, 2023 at 16:26
  • @Amax1 you can use async/await or promise/then, but either way, it is asynchronous. Anything outside of the then will run before anything inside the then.
    – sfdcfox
    Jul 19, 2023 at 17:28

You must log in to answer this question.

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