Recommended ESLint Rules for Writing Better Asynchronous JavaScript Code
This article introduces a set of ESLint rules that help developers write safer and more efficient asynchronous JavaScript by avoiding common pitfalls such as async promise executors, awaiting in loops, improper promise handling, and callback-related race conditions.
Hello everyone, I'm ConardLi. Today I recommend several best practices for writing asynchronous JavaScript code, each accompanied by a corresponding ESLint rule that you can configure.
no-async-promise-executor
It is discouraged to pass an async function to the new Promise constructor because wrapping an async function in a Promise is usually unnecessary and can hide errors.
// ❌
new Promise(async (resolve, reject) => {});
// ✅
new Promise((resolve, reject) => {});Using async inside the Promise constructor may prevent thrown exceptions from rejecting the promise.
no-await-in-loop
Awaiting inside a loop prevents concurrent execution and underutilizes JavaScript's event‑driven 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);no-promise-executor-return
Returning a value from a Promise executor is useless; instead resolve or reject with the appropriate value.
// ❌
new Promise((resolve, reject) => {
return result;
});
// ✅
new Promise((resolve, reject) => {
resolve(result);
});require-atomic-updates
Combining assignment with await can cause race conditions, leading to unpredictable values such as a total count that may be 3 or 5 instead of the expected 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; // read‑modify‑write is atomic here
}
await Promise.all([addPosts(1), addPosts(2)]);
console.log('Post count:', totalPosts);max-nested-callbacks
Avoid deep callback nesting; refactor callbacks into Promises and use async/await for clearer code.
/* eslint max-nested-callbacks: ["error", 3] */
// ❌
async1((err, result1) => {
async2(result1, (err, result2) => {
async3(result2, (err, result3) => {
async4(result3, (err, result4) => {
console.log(result4);
});
});
});
});
// ✅
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, you can omit the redundant await unless you need to catch errors with try/catch.
// ❌
async () => {
return await getUser(userId);
};
// ✅
async () => {
return getUser(userId);
};prefer-promise-reject-errors
Always reject Promises 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 errors.
// ❌
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 I/O in Node.js 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 handle rejections.
// ❌
myPromise().then(() => {});
// ✅
myPromise().then(() => {}).catch(() => {});@typescript-eslint/no-misused-promises
Do not pass a Promise directly to places expecting a boolean or other non‑Promise value; await it first.
// ❌
if (getUserFromDB()) {}
// ✅
if (await getUserFromDB()) {}Reference: https://maximorlov.com/linting-rules-for-asynchronous-code-in-javascript/
Sohu Tech Products
A knowledge-sharing platform for Sohu's technology products. As a leading Chinese internet brand with media, video, search, and gaming services and over 700 million users, Sohu continuously drives tech innovation and practice. We’ll share practical insights and tech news here.
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.