This rule raises an issue when async operations are wrapped in immediately invoked function expressions, promise chains, or async functions that are called immediately at the top level.
JavaScript’s top-level await feature allows you to use await directly at the module level without wrapping async operations in
functions. This makes code cleaner and more readable.
When you wrap async operations in immediately invoked function expressions (IIFEs) or create async functions just to call them immediately, you add unnecessary boilerplate code. This pattern was common before top-level await was available, but is no longer needed in modern JavaScript environments.
The same applies to top-level promise chains such as task().then(…).catch(…), which can be rewritten more directly with top-level
await and try/catch.
Using wrapper patterns can also make error handling more complex and may lead to unhandled promise rejections if not properly managed. Top-level await provides a more straightforward approach to handling async operations at the module level.
Using wrapper patterns or promise chains instead of top-level await reduces code readability and maintainability. It adds unnecessary complexity and boilerplate code that makes the intent less clear. In some cases, improper error handling in wrapper patterns can lead to unhandled promise rejections.
Replace immediately invoked async function expressions or top-level promise chains with top-level await.
fetch('config.json')
.then(response => response.json())
.then(config => applyConfig(config))
.catch(logError); // Noncompliant
try {
const response = await fetch('config.json');
const config = await response.json();
applyConfig(config);
} catch (error) {
logError(error);
}
(async () => {
try {
await run();
} catch (error) {
console.error(error);
process.exit(1);
}
})(); // Noncompliant
try {
await run();
} catch (error) {
console.error(error);
process.exit(1);
}