0

I am using Javascript file to import in LWC. Here the constructor variable _testurl . I couldn't read that variable in getAccount() . can someone help throw some light I am struck in this for long time? Thank you in advance.

import getsettings from "@salesforce/apex/CustomSettingContrl.getsettings";

export default class serviceclass {


  constructor() {       
    getsettings({ Active: true }).then((result) => {
      this._testurl = JSON.stringify(result);
    });
  }

  getAccount()
  {
    console.log('urlhere' +  this._testurl );
    console.log('thisopertor' + JSON.stringify(this));
  }
}

LWC js

import servicelwc from "c/serviceclass";
export default class AccountInfo extends LightningElement
{

  getAccount()
  {
    this.service=new servicelwc();  //instance of class    
    this.connection.getAccount(); //invoking method
  }
}

Edit: One thing I am certain is the imperative method is async operation so the variable _testurl not initialized . so I am looking to use promise async operation but in vain.

export default class serviceclass {
 constructor() {
    this._testurl = this.asyncDemo().then((result) => {
      return result;
    });
    console.log("construtorurl" + this._testurl);
  }

  async asyncDemo() {
    console.log("asyncDemo" + "This is asyncDemo");
    return "This is asyncDemo";
  }
}
1
  • any one has answer? – sfnerd Oct 27 '20 at 14:57
0

There are many possible ways to solve this problem by incorporating into design level changes and best practices. As a thumb rule, never use async operations and .then inside constructor method (unless you have very specific requirements and have a fair understanding about how async executions work under the hood). Without getting into all that, one way to solve this problem in your current code is to have it as shown below:

LWC JS

import servicelwc from "c/serviceclass";

export default class AccountInfo extends LightningElement
{    
    async getAccount(){
        this.service=new servicelwc();  //instance of class
        this.test = await this.service.getAccount(); //invoking method
    }
}

Serviceclass JS

import getsettings from "@salesforce/apex/CustomSettingContrl.getsettings";

export default class Serviceclass
{        
    async getAccount(){
        var result  = await getsettings({ Active: true });
        this._testurl = JSON.stringify(result);
        return this._testurl;
    }
}

If you are interested in knowing why your code was not fetching expected results, read the section below. Otherwise, you can ignore it.


Javascript is essentially a single-threaded execution, which to a layman would appear to be multi-threaded. But this multi-thread execution is emulated with the help of stack, queue and event loop by Javascript runtime engine. Understanding this concept is important to see why your code doesn't work. However, explaining this is beyond the scope of this question. There are a lot of videos & articles that explain this concept and I would recommend you to watch this video at your convenience.

Serviceclass JS: I'm not sure if there was any specific reason to implement the JS utility this way, but I wouldn't prefer to do it this way.

  • I would recommend you create JS functions instead and export them as named functions, as explained here.
  • It's not a good practice to place apex calls (or async calls) inside the constructor.
  • If there no additional functionality in this utility class (other than calling CustomSettingContrl.getsettings), I would recommend you to call this via @wire adapter directly in the lightning web component.

How does your code execute during runtime? You are correct about the async execution of getsettings within the Serviceclass constructor. So, the apex results are not fetched until the other codes complete executing.

In your first approach, the sequence of execution will be something like this:

  1. AccountInfo: getAccount() [Start of method execution]
  2. AccountInfo: this.service=new servicelwc();
  3. serviceclass.constructor: getsettings [At this point constructor invokes the apex call, but the .then part of parsing the apex results is put on hold until rest of the JS code execution would complete. Note that the .then part will not execute immediately even if apex returns the result before the rest of the JS code execution is completed.]
  4. AccountInfo.getAccount(): this.service.getAccount();
  5. serviceclass.constructor: getAccount [this._testurl is not yet available since the apex results have not be received and parsed. So, it will be undefined or null.]
  6. AccountInfo: getAccount() [End of method execution]
  7. serviceclass.constructor: (result) => { this._testurl = JSON.stringify(result); }

In your second approach, making use of async keyword on a JS function doesn't really make it asynchronous, but it only tells the JS runtime engine that this function is marked for async calls. You need to use await keyword within the function to actually make it asynchronous and the runtime would wait for completion of any line that is marked with await (before the next line of code is executed). In your code, you have used async function without await keyword and .then within the constructor. So, the sequence of execution will be same as the first approach. Note that simply using await keyword in async function (without any code change in the LWC JS) won't help you either. For example, assume you have the following snippet:

async asyncDemo() {
    var result = await getsettings({ Active: true });
    this._testurl = JSON.stringify(result);
}

The second line of this code function would wait for execution until the getsettings apex method returns the result, but the code in the LWC would have completed executing before this.

1
  • Thank you so much. It is very helpful – sfnerd Oct 29 '20 at 4:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.