This rule raises an issue when an async function does not use any asynchronous feature.
Declaring a function async signals that it suspends on await or for await, so other work can proceed while
an operation such as I/O completes.
If the function’s body never suspends, the async keyword is superfluous. Using async unnecessarily can:
async function always returns a Promise, even when its body runs
entirely synchronously. Wrapping a synchronous result in a Promise costs at least one microtask tick that a plain synchronous return
would not.await the result or chain .then() to read it, ceremony that is
unnecessary when the function was never truly asynchronous.Remove the async keyword. If the function’s return type must remain a Promise, for example to satisfy an interface or
base class, wrap the return value with Promise.resolve() instead of marking the function async.
async function greet(name) { // Noncompliant: never uses "await"
console.log(`Hello, ${name}`);
}
function greet(name) { // Compliant
console.log(`Hello, ${name}`);
}
async function getUserName(user) { // Noncompliant: nothing is awaited, "async" only wraps the result in a Promise
return user.name;
}
function getUserName(user) {
return user.name;
}
interface Repository {
save(entity: Entity): Promise<void>;
}
class InMemoryRepository implements Repository {
async save(entity: Entity): Promise<void> { // Noncompliant: "async" only wraps the return value in a Promise
this.entities.push(entity);
}
}
interface Repository {
save(entity: Entity): Promise<void>;
}
class InMemoryRepository implements Repository {
save(entity: Entity): Promise<void> { // Compliant: satisfies the contract without "async"
this.entities.push(entity);
return Promise.resolve();
}
}
This rule does not raise an issue in the following cases:
async function noop() {}.return statement or a concise arrow body, such as async () ⇒
somePromise, that resolves to an already-thenable value counts as equivalent to awaiting it, and so does a for await loop.async function* that yields a thenable value, or that
delegates via yield* to an async-iterable source, counts as using an async feature even without an explicit await.
async function stub() {} // Compliant: empty body
async function relay(id) {
return fetchUser(id); // Compliant: the return value is already a Promise
}
async function* stream(source) {
yield* source; // Compliant: delegates to an async-iterable source
}
Requiring a Promise return type through an interface, decorator, or base class does not exempt a method from this rule. Using
async only to coerce a non-promise return value into a Promise, with no actual suspension in the body, is exactly the
pattern this rule targets. See the Repository example above for the recommended fix.