eslint-plugin-require-await-for-promise
🧵 Backstory
I didn’t plan to write an ESLint rule.
It started with one forgotten await. Then another. Then one inside a return with a logical expression. Then a bug that only appeared in production — because a Promise silently failed.
After reviewing pull request after pull request with comments like:
“Hey, you forgot to await this.”
“You’re calling an async function but ignoring the result.”
“This may run in parallel and break stuff.”
…I finally decided to do something about it.
So I built a custom ESLint rule that enforces what teams often assume: if you call a Promise-returning function, you’d better handle it.
💥 I Got Tired of Missing await – So I Wrote an ESLint Plugin That Forces You to Handle Promises Properly
😫 Tired of Silent Failures?
You call an async function.
You forget to await.
- No errors.
- No warnings.
- No stack trace.
- Just... bugs. That’s the painful reality of JavaScript and TypeScript development with Promises.
We’ve all done it:
doSomethingAsync(); // 👋 no await, no then, no catch — and no error
This line quietly executes. The promise silently starts. If it fails — and no one’s watching — your app may break in weird ways.
🧨 Real-World Pain
In my day-to-day work, I’ve seen these kinds of bugs:
- Missed errors from unhandled API calls
- Double-click bugs because async handlers weren’t awaited
- Race conditions caused by out-of-order promise execution
- Code that "just works" in dev but explodes in production
And in reviews, I kept commenting:
"You forgot to await this"
"Please add.catch()"
"Is this handled?"
Eventually, I snapped.
🛠️ So I Built a Rule: require-await-for-promise
Meet eslint-plugin-require-await-for-promise — a TypeScript-aware ESLint plugin that forces you to handle every Promise call.
It warns if you call any function that returns a Promise but don’t:
-
awaitit - chain
.then(),.catch(), or.finally() - return it
- wrap it in
Promise.all,Promise.race, etc.
✅ Good Code (Handled Promises)
await fetchUser();
fetchUser().then(console.log);
return fetchUser();
await Promise.all([
fetchUser(),
fetchPosts()
]);
return await fetchUser() || await fetchDefaultUser();
❌ Bad Code (Untracked Promises)
fetchUser(); // ❌ No await
return fetchUser() || fetchDefaultUser(); // ❌ Both unhandled
fetchData() || console.log('fallback'); // ❌ Even worse
return callA() || callB() || callC(); // ❌ Multiple chances to lose control
callWithoutAwait().catch(() => {}); // ✅ handled
💡 Why This Rule Helps
This ESLint rule has saved me and my team hours of debugging. It:
- Prevents "fire-and-forget" async calls
- Forces you to acknowledge side effects
- Guards against accidental Promise loss in logical expressions
- Promotes explicit and predictable async code
It catches bugs before they reach runtime.
And it's especially useful in large codebases where async behavior is subtle, or contributors may be less experienced with Promises.
🔍 How It Works
The plugin uses @typescript-eslint/utils and the TypeScript type checker to:
- Analyze every function call
- Determine if it returns a
Promise<...> - Check its surrounding context
- Exclude safe patterns like
return fetch(),Promise.all(...), etc.
It even walks up AST trees to detect logical expressions like a() || b() in return statements — a common source of bugs.
⚙️ Installation
npm install --save-dev eslint-plugin-require-await-for-promise
Then in your ESLint config (JSON or Flat Config):
{
"plugins": ["require-await-for-promise"],
"rules": {
"require-await-for-promise/require-await-for-promise": "error"
},
"parser": "@typescript-eslint/parser",
"parserOptions": {
"project": "./tsconfig.json"
}
}
⚠️
parserOptions.projectis required so the rule can analyze types.
🧪 Want to Try It?
Here’s a complete test function you can lint:
async function test(): Promise<void> {
delayedThing(); // ❌ Error
await delayedThing(); // ✅ OK
delayedThing().then(() => {}); // ✅ OK
return delayedThing(); // ✅ OK
return delayedThing() || delayedThing(); // ❌ Error
}
📦 Package Links
🤝 Contributions Welcome!
Have ideas, edge cases, or want to improve this rule? Open an issue or PR on GitHub.
If this plugin saved you time — star the repo and share it with others who hate silent async bugs as much as I do.
Happy linting! 🚀
Top comments (1)
I've already downloaded this - we have exactly what we need at our company on express!
There is no autofix. But it's not a problem - everything is highlighted and you can simply fix it