6

UPDATED: This question also helped me understand better.

I have few apex method calls imperatively and its working as expected but I'm not sure if there is a better way to handle or has any pattern to follow.

Is this okay to chain the calls like how I'm doing here?

Here is my code:

handleSaveMethod() { 

     createEmployee({ recordId : this.recordId, employee : 
                                 this.employeeObj })
            .then(result => {
                this.error = undefined;
                if(result == true) {
                    //create employee history  
                    createEmployeeHistoryRecords({employeeHistoryRecords 
                                   : this.listOfEmployeesObj})
                        .then(result => {
                            console.log(result);
                            //do more stuffs.....
                         }
                }
             }
}

2 Answers 2

6

There's a better way: async and await. It avoids the deeply nested calls.

async handleSaveMethod() { 
  try {
    let employeeCreated = await createEmployee({ recordId : this.recordId, employee: this.employeeObj })
    if(!employeeCreated) {
      return;
    }
    let employeeHistory = await createEmployeeHistoryRecords({employeeHistoryRecords: this.listOfEmployeesObj})
    // etc
  } catch (e) {
    // deal with any errors
  }
}

You can also call code in parallel, when appropriate:

async handleSaveMethod() { 
    try {
        let employeePromise = createEmployee({ recordId : this.recordId, employee: this.employeeObj })
        let employeeHistoryPromise = createEmployeeHistoryRecords({employeeHistoryRecords: this.listOfEmployeesObj})
        await Promise.all([employeePromise, employeeHistoryPromise]);
    } catch (e) {
        // deal with any errors
    }
}

Note that this code is still kind of awkward; if there's a failure, your code should throw an exception so that you go to the catch block.

Also, which would make it simply:

async handleSaveMethod() { 
  try {
    await createEmployee({ recordId : this.recordId, employee: this.employeeObj })
    await createEmployeeHistoryRecords({employeeHistoryRecords: this.listOfEmployeesObj})
    // etc
  } catch (e) {
    // deal with any errors
  }
}

You can, and should, throw AuraHandledException if the method call is pass/fail, rather than returning a Boolean value (as in your demonstration).

5
  • thank you appreciate your feedback, you have three different methods and I see the last method is the preferable right?
    – Nick
    Dec 4, 2021 at 20:04
  • @NickKahn Yes, throwing exceptions keeps the code as simple as possible. It's how I write my own code.
    – sfdcfox
    Dec 4, 2021 at 20:06
  • great, can you use AuraHandledException in LWC?
    – Nick
    Dec 4, 2021 at 20:08
  • also, I'm showing ShowToastEvent once the employee history record is created , I believe I can show the toast event right after await createEmployeeHistoryRecords
    – Nick
    Dec 4, 2021 at 20:13
  • @NickKahn You throw the AuraHandledException in Apex code to cause the catch block to be executed in the examples above. And yes, you can show the success toast immediately after the await statement; once you reach that line of code, you know your method has completed successfully.
    – sfdcfox
    Dec 4, 2021 at 20:21
4

Other than the methods explained by sfdcfox, there's a way to avoid pyramid of doom without using async and await: just return a new promise in then() method.

handleSaveMethod() { 
    this.error = undefined;
    createEmployee({ recordId : this.recordId, employee : this.employeeObj })
        .then((employeeResult) => {
            if(employeeResult) {
                return createEmployeeHistoryRecords({employeeHistoryRecords: this.listOfEmployeesObj});
            }
        })
        .then((employeeHistoryResult) => {
            // do more stuff
            return something();
        })
        .then((somethingResult) => 
            // no returns here, but it isn't an error 
            more();
        )
        .then((moreResult) => {
            // and so on
        })
        .catch((error) => {
            // If any error happens, it will be handled here
            this.error = error;
        });
}

In then((somethingResult) there is no need to write return more(); because () => x is short for () => { return x; })

Do not forget that promise provides a finally() method too. It runs when the promise is settled (no matter if it's fulfilled or rejected), so it can be used to avoid duplicating code in both then() and catch().

You could read more about it on MDN - Using Promises.

You must log in to answer this question.

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