面试题答案
一键面试1. 内存管理方面
- 合理使用 try - catch:避免在高频率执行的异步回调中过度使用
try - catch
。例如,在处理大量请求的路由处理函数中,若频繁使用try - catch
可能会导致内存开销增加。可以将异步操作封装成 Promise,利用Promise.catch
统一处理异常,这样减少栈开销。 - 对象复用:在错误处理模块中,对于一些固定的错误对象(如自定义的错误类型实例),可以复用而不是每次都创建新的。比如,定义一个全局的
BusinessError
类,在需要抛出业务相关错误时复用其实例。
2. 异步处理方面
- 利用 async/await 与 Promise 组合:使用
async/await
语法糖来处理异步操作,它基于 Promise 构建,能让异步代码看起来像同步代码,便于捕获异常。例如:
app.get('/example', async (req, res) => {
try {
const result = await someAsyncOperation();
res.send(result);
} catch (error) {
handleError(error, res);
}
});
- 并发控制:对于高并发场景下的多个异步任务,使用
Promise.allSettled
或Promise.race
来控制并发数量,避免过多的异步任务同时执行导致资源耗尽。比如,在一个需要同时调用多个 API 的场景中:
const apiCalls = [apiCall1(), apiCall2(), apiCall3()];
Promise.allSettled(apiCalls).then(results => {
results.forEach((result, index) => {
if (result.status === 'rejected') {
// 处理错误
handleError(result.reason, res);
}
});
});
3. 资源释放方面
- 及时关闭连接:在数据库连接、文件系统操作等场景下,确保在操作完成或出现异常时及时关闭连接或释放资源。例如,使用
mysql2
连接 MySQL 数据库时:
const mysql = require('mysql2');
const connection = mysql.createConnection({ /* 配置 */ });
connection.connect();
app.get('/data', async (req, res) => {
try {
const [rows] = await new Promise((resolve, reject) => {
connection.query('SELECT * FROM your_table', (error, results) => {
if (error) {
reject(error);
} else {
resolve(results);
}
});
});
res.send(rows);
} catch (error) {
handleError(error, res);
} finally {
connection.end();
}
});
- 清理定时器:如果应用中使用了
setTimeout
或setInterval
,在不需要时及时清理。比如,在一个定时任务模块中:
let timer;
function startTask() {
timer = setTimeout(() => {
try {
// 执行任务
} catch (error) {
handleError(error);
}
}, 5000);
}
function stopTask() {
clearTimeout(timer);
}
实际项目经验
在一个实时数据推送的 Node.js Express 应用中,最初在处理 WebSocket 连接和数据处理的异步操作时,没有统一的异常捕获机制,导致部分未捕获异常使得进程崩溃。优化时,采用了上述基于 async/await
和 Promise.catch
的异常捕获策略,并在数据库连接和 WebSocket 连接的资源管理上做了改进。通过这些措施,应用的稳定性得到了显著提升,崩溃次数从每周数次降低到几乎为零,同时由于减少了不必要的内存开销和资源竞争,性能也有所提高,在高并发场景下的响应时间缩短了约 20%。