JavaScript Async Debugging Case Study
이 글의 핵심
Production Node.js: tracking down intermittent UnhandledPromiseRejection—Promise chains, async/await, error boundaries, and observability with Sentry.
Introduction
UnhandledPromiseRejection is one of the most common Node.js warnings. This post walks through finding and fixing errors in real async code.
What you will learn
- Why Promise errors “disappear”
- How to improve async stack traces
- async/await error-handling patterns
- Production monitoring (e.g. Sentry)
Table of contents
- Problem: intermittent unhandled rejections
- Symptom: errors vanish
- Tracing Promise chains
- Root cause: missing error handler
- Fix 1: async/await
- Fix 2: global handlers
- Fix 3: error boundary pattern
- Monitoring: Sentry
- Closing thoughts
1. Problem
Logs
(node:12345) UnhandledPromiseRejectionWarning: Error: Database connection failed
at Database.connect (database.js:45:15)
Traits
- Hard to repro locally
- A few times per day
- Little context on caller
2. Symptom
Buggy route
app.get('/users/:id', (req, res) => {
getUserData(req.params.id)
.then(user => {
res.json(user);
});
// Missing .catch()
});
async function getUserData(id) {
const user = await db.query('SELECT * FROM users WHERE id = ?', id);
if (!user) {
throw new Error('User not found');
}
return user;
}
Why it fails silently at the edge
Rejection propagates; without .catch (or try/catch in an async handler), Node reports UnhandledPromiseRejection.
3. Tracing
{
"scripts": {
"start": "node --trace-warnings --async-stack-traces server.js"
}
}
Improved stacks often show the route file and line.
4. Root cause
Common patterns:
.thenwithout.catchasynchandler without try/catch and no Express error forward.catchthat only logs and swallows errors needed downstream
5. Fix 1: async/await
app.get('/users/:id', async (req, res) => {
try {
const user = await getUserData(req.params.id);
res.json(user);
} catch (err) {
console.error('Error fetching user:', err);
res.status(500).json({ error: 'Internal server error' });
}
});
6. Fix 2: global handlers
process.on('unhandledRejection', (reason, promise) => {
console.error('Unhandled Rejection at:', promise, 'reason:', reason);
errorLogger.log({
type: 'unhandledRejection',
reason: reason,
stack: reason.stack,
timestamp: new Date().toISOString(),
});
});
process.on('uncaughtException', (err) => {
console.error('Uncaught Exception:', err);
process.exit(1);
});
Express wrapper
function asyncHandler(fn) {
return (req, res, next) => {
Promise.resolve(fn(req, res, next)).catch(next);
};
}
app.get('/users/:id', asyncHandler(async (req, res) => {
const user = await getUserData(req.params.id);
res.json(user);
}));
app.use((err, req, res, next) => {
console.error('Error:', err);
res.status(500).json({ error: err.message });
});
7. Fix 3: service layer
Map infrastructure errors to domain errors (NotFoundError, ServiceUnavailableError) and handle by type in one error middleware.
8. Monitoring
const Sentry = require('@sentry/node');
Sentry.init({
dsn: process.env.SENTRY_DSN,
tracesSampleRate: 1.0,
});
app.use(Sentry.Handlers.requestHandler());
app.use(Sentry.Handlers.tracingHandler());
app.use(Sentry.Handlers.errorHandler());
9. Patterns
Promise.allSettledvs barePromise.allwhen partial failure is OK- Timeouts with
Promise.race - Retries with exponential backoff
Closing thoughts
- Every async path needs a rejection handler (
.catchor try/catch orasyncHandler) - Global safety net for slips
- Sentry (or similar) for production
- async/await for readability Assume errors will happen—design the path explicitly.
FAQ
Q1. Promise chains vs async/await?
Prefer async/await with try/catch at boundaries; chains are fine with explicit .catch.
Q2. try/catch everywhere?
Centralize at HTTP layer + domain boundaries; don’t wrap every line.
Q3. Should unhandled rejections kill the process?
Modern Node may exit; use process managers and fix root causes.
Related posts
Keywords
JavaScript, async, Promise, async/await, Unhandled Rejection, error handling, debugging, Sentry, Node.js, case study
같이 보면 좋은 글 (내부 링크)
이 주제와 연결되는 다른 글입니다.
- JavaScript 비동기 프로그래밍 | Promise, async/await 완벽 정리
- JavaScript 비동기 디버깅 실전 사례 | Promise 체인 에러 추적하기
- [JavaScript Promise & async/await Complete Guide](/en/blog/javascript-promise-async-await-guide/
이 글에서 다루는 키워드 (관련 검색어)
JavaScript, Async, Promise, async/await, Debugging, Error Handling, Case Study 등으로 검색하시면 이 글이 도움이 됩니다.