Recommended ESLint Rules for Writing Good Asynchronous JavaScript Code
This article presents a collection of ESLint rules for JavaScript asynchronous programming, explaining why patterns like async promise executors, awaiting in loops, returning values from Promise constructors, and others should be avoided, and provides correct code examples to improve readability, performance, and error handling.
Today we recommend several best‑practice ESLint rules for writing good asynchronous JavaScript code; each rule targets a specific scenario and can be enabled in your ESLint configuration.
no-async-promise-executor
Do not pass an async function to the new Promise constructor because the resulting Promise may not reject on errors and the extra wrapper is unnecessary.
// ❌
new Promise(async (resolve, reject) => {});
// ✅
new Promise((resolve, reject) => {});Using async inside the constructor prevents proper error propagation.
no-await-in-loop
Avoid using await inside loops; it blocks the event loop and defeats JavaScript's concurrency model.
// ❌
for (const url of urls) {
const response = await fetch(url);
}
// ✅
const responses = [];
for (const url of urls) {
const response = fetch(url);
responses.push(response);
}
await Promise.all(responses);Running the async tasks concurrently greatly improves execution efficiency.
no-promise-executor-return
Do not return a value from a Promise executor; the return value is ignored and does not affect the Promise state.
// ❌
new Promise((resolve, reject) => {
return result;
});
// ✅
new Promise((resolve, reject) => {
resolve(result);
});require-atomic-updates
Combining assignment with await can cause race conditions. For example, the final value of totalPosts may be 3 or 5 instead of 8.
// ❌
let totalPosts = 0;
async function addPosts(userId) {
totalPosts += await getPosts(userId);
}
await Promise.all([addPosts(1), addPosts(2)]);
console.log('Post count:', totalPosts);
// ✅
let totalPosts = 0;
async function addPosts(userId) {
const posts = await getPosts(userId);
totalPosts += posts; // variable is read and immediately updated
}
await Promise.all([addPosts(1), addPosts(2)]);
console.log('Post count:', totalPosts);max-nested-callbacks
Prevent callback hell by limiting nesting depth and refactoring callbacks into Promise chains with async/await .
// ❌ (deeply nested callbacks)
async1((err, result1) => {
async2(result1, (err, result2) => {
async3(result2, (err, result3) => {
async4(result3, (err, result4) => {
console.log(result4);
});
});
});
});
// ✅ (sequential awaits)
const result1 = await asyncPromise1();
const result2 = await asyncPromise2(result1);
const result3 = await asyncPromise3(result2);
const result4 = await asyncPromise4(result3);
console.log(result4);no-return-await
When returning a Promise from an async function, omit the unnecessary await unless you need to catch errors inside a try…catch block.
// ❌
async () => {
return await getUser(userId);
};
// ✅
async () => {
return getUser(userId);
};prefer-promise-reject-errors
When rejecting a Promise, always reject with an Error object to preserve stack traces.
// ❌
Promise.reject('An error occurred');
// ✅
Promise.reject(new Error('An error occurred'));node/handle-callback-err
In Node.js callbacks, always handle the first err argument to avoid uncaught exceptions.
// ❌
function callback(err, data) {
console.log(data);
}
// ✅
function callback(err, data) {
if (err) {
console.log(err);
return;
}
console.log(data);
}node/no-sync
Avoid synchronous Node.js APIs when asynchronous alternatives exist, as sync calls block the event loop.
// ❌
const file = fs.readFileSync(path);
// ✅
const file = await fs.readFile(path);@typescript-eslint/await-thenable
Do not await non‑Promise values; ensure the awaited expression returns a Promise.
// ❌
function getValue() {
return someValue;
}
await getValue();
// ✅
async function getValue() {
return someValue;
}
await getValue();@typescript-eslint/no-floating-promises
Always attach a .catch handler to Promises to avoid unhandled rejections.
// ❌
myPromise()
.then(() => {});
// ✅
myPromise()
.then(() => {})
.catch(() => {});@typescript-eslint/no-misused-promises
Do not use a Promise directly in a boolean context; await it first or assign it to a variable.
// ❌
if (getUserFromDB()) {}
// ✅
if (await getUserFromDB()) {}
// Better
const user = await getUserFromDB();
if (user) {}Reference: Linting rules for asynchronous code in JavaScript
IT Services Circle
Delivering cutting-edge internet insights and practical learning resources. We're a passionate and principled IT media platform.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.