Then vs Async Await in LWC
Hi there! π
This post expands on the topic of Promises in LWC.
JavaScript offers then
and await
as tools for managing asynchronous code. The then
keyword is used with Promises and allows you to specify what should happen after a Promise is fulfilled. On the other hand, the await
keyword can only be used within an asynchronous function and suspends the execution of the function until the specified Promise is resolved.
In this post, we’ll dive deeper into the usage and benefits of then
and await
in JavaScript.
Let’s begin!
Then vs Await
then/catch
and async/await
are very similar. Both are used to resolve the Promise.
What are the differences?
- With
await
JavaScript will pause the function execution until the promise settles (resolve, reject). In short: asynchronous code behaves like synchronous.
function myPromiseFunction() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Success');
}, 2000);
});
}
(async () => {
console.log('Start');
const result = await myPromiseFunction();
console.log(`result - ${result}`);
console.log('End');
})();
// Output
// Start
// result - Success
// End
- With
then
the rest of the function will continue to execute, but JavaScript won’t execute thethen()
callback until the promise settles (resolve, reject).
function myPromiseFunction() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Success');
}, 2000);
});
}
(() => {
console.log('Start');
myPromiseFunction()
.then(result => {
console.log(`result - ${result}`);
})
.catch(error => {
console.error(error);
})
console.log('End');
})();
// Output
// Start
// End
// result - Success
Promises Everywhere
Where we can find promises?
Apex Method returns a Promise
The apex method returns a Promise.
import apexMethodName from '@salesforce/apex/Namespace.Classname.apexMethodReference';
// Then/Catch
apexMethodName
.then(result => {
console.log(result);
})
.catch(error => {
console.error(error);
});
// Async/Await
try {
const result = await apexMethodName();
} catch(error) {
console.error(error);
}
Async
The async
method returns a Promise.
async function myAsyncFunction() {
return 'hello async promise!';
}
// Then
myAsyncFunction()
.then(result => console.log(result));
// Await
const result = await myAsyncFunction();
Then
then
return a Promise.
It immediately returns an equivalent Promise object, allowing you to chain calls to other promise methods. ~ MDN Web Docs
Each call to then()
creates another step in the Promise chain, and if thereβs an error at any point in the chain, the next catch()
block will be triggered.
function myPromiseFunction() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Success');
}, 2000);
});
}
function myThenFunction() {
return myPromiseFunction()
.then(result => {
return result;
});
}
// Then
myThenFunction()
.then(result => console.log(result));
// Await
const result = await myThenFunction();
Best practices
Do Not Combine Then And Await
As you know for the previous paragraphs then\catch
and async\await
are used to resolve Promises. You also know that then
and async
return a Promise. So?
We can combine then\catch
and async\await
and we can do something like that:
await myPromise().then(result => {
console.log(result);
});
Let’s add some context here:
βββ
function myPromise() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Success');
}, 2000);
});
}
async function myFun() {
return await myPromise().then(result => {
console.log(result);
});
}
myFun();
It works! π₯³ But does it mean that, it is correct?
No.
You should NOT combine then/catch and async/await!
- Code is confusing. Devs who are not familiar with Promises very well, will not follow the code flow.
- It’s hard to understand. What we return here? A result, because of
await
or a Promise, becausethen
is a last statement? - Keep It Simple, Stupid (KISS) rule is broken here. You do NOT need constructions like that in your code. Do not mix
then
andawait
for the same Promise.
How to do it right?
β β β
function myPromise() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Success');
}, 2000);
});
}
// async function always return a promise
async function myFunAwait() {
const result = await myPromise();
console.log(`Await ${result}`);
}
// then function always return a promise
function myFunThen() {
return myPromise().then(result => {
console.log(`Then ${result}`);
});
}
console.log('Start');
await myFunAwait(); // you can also use then/catch to resolve
await myFunThen(); // you can also use then/catch to resolve
console.log('End');
// Output
// Start
// Await Success
// Then Success
// End
Creating New Promises
You shouldn’t need to create a promise explicitly to manually call resolve or reject. You shouldn’t wrap a Promise within a Promise.
βββ
import apexMethodName from '@salesforce/apex/Namespace.Classname.apexMethodReference';
callApex() {
return new Promise((resolve, reject) => {
apexMethodName
.then(result => {
resolve(result);
})
.catch(error => {
reject(error);
});
})
}
β β β
The refactor here is actually pretty simple:
import apexMethodName from '@salesforce/apex/Namespace.Classname.apexMethodReference';
async callApex() {
const result = await apexMethodName();
// when error occurs reject will be call
return result; // when success resolve will be call
}
Nested Promises
Nested promises in JavaScript can lead to "callback hell" or code that is difficult to read and maintain. Nested promises can create a chain of asynchronous code that is hard to follow and debug. Additionally, if an error occurs in a nested promise, it can be difficult to properly handle and propagate the error to a higher-level error handler. This can result in silent failures or unexpected behavior in the application.
βββ
async function getOrderDetailsWrapper(userId) {
getUserData(userId)
.then(user => {
return getUserOrders(user.orderIds)
.then(orders => {
return getOrderDetails(orders[0].orderId)
.then(details => {
console.log(details);
})
})
})
.catch(error => {
console.error(error);
});
};
getOrderDetailsWrapper(userId);
β β β
async function getOrderDetailsWrapper(userId) {
try {
const user = await getUserData(userId);
const orders = await getUserOrders(user.orderIds);
const details = await getOrderDetails(orders[0].orderId);
console.log(details);
} catch (error) {
console.error(error);
}
};
getOrderDetailsWrapper(userId);
If you have any questions, feel free to ask in the comment section below. π
Was it helpful? Check out our other great posts here.