您现在的位置是:网站首页 > 代码优化技巧文章详情
代码优化技巧
陈川
【
Node.js
】
21046人已围观
4918字
Node.js作为高效的JavaScript运行时,代码优化直接影响性能与可维护性。以下是针对异步控制、内存管理、算法选择等场景的具体实践方案。
异步流程优化
避免回调地狱是Node.js优化的首要任务。util.promisify
可将回调函数快速转为Promise:
const fs = require('fs');
const util = require('util');
const readFile = util.promisify(fs.readFile);
async function processFiles() {
try {
const data = await readFile('config.json');
return JSON.parse(data);
} catch (err) {
console.error('文件处理失败', err);
}
}
对于并行任务,优先使用Promise.allSettled
而非Promise.all
:
const API_CALLS = [
fetchUserData(),
fetchConfig(),
fetchPermissions()
];
const results = await Promise.allSettled(API_CALLS);
const successfulData = results
.filter(r => r.status === 'fulfilled')
.map(r => r.value);
内存泄漏防范
闭包不当使用是常见泄漏源。以下代码会导致timer持续持有作用域:
function createLeak() {
const hugeArray = new Array(1e6).fill('*');
setInterval(() => {
console.log(hugeArray.length); // 闭包持有hugeArray
}, 1000);
}
改用WeakMap解决:
const wm = new WeakMap();
function noLeak() {
const data = { largeData: new Array(1e6) };
wm.set(data, 'metadata');
setInterval(() => {
console.log(wm.get(data)); // 弱引用可被GC回收
}, 1000);
}
算法时间复杂度优化
数据库查询时常见O(n²)问题:
// 低效写法
async function getUserWithPosts() {
const users = await User.findAll();
return Promise.all(users.map(async user => {
const posts = await Post.findAll({ where: { userId: user.id } });
return { ...user.toJSON(), posts };
}));
}
改为批量查询降低复杂度:
// 优化版本
async function optimizedQuery() {
const users = await User.findAll();
const userIds = users.map(u => u.id);
const allPosts = await Post.findAll({
where: { userId: userIds },
attributes: ['id', 'userId', 'content']
});
return users.map(user => ({
...user.toJSON(),
posts: allPosts.filter(p => p.userId === user.id)
}));
}
流式处理大文件
传统文件读取方式会导致内存暴涨:
// 危险操作
fs.readFile('huge.log', (err, data) => {
if (err) throw err;
processData(data); // 一次性加载到内存
});
改用流处理:
const { pipeline } = require('stream');
const zlib = require('zlib');
pipeline(
fs.createReadStream('huge.log'),
zlib.createGzip(), // 压缩中转
fs.createWriteStream('huge.log.gz'),
(err) => {
if (err) console.error('处理失败', err);
}
);
缓存策略实现
内存缓存基础实现:
class SimpleCache {
constructor(ttl = 60) {
this.store = new Map();
this.ttl = ttl * 1000;
}
set(key, value) {
this.store.set(key, {
timestamp: Date.now(),
value
});
}
get(key) {
const entry = this.store.get(key);
if (!entry) return null;
if (Date.now() - entry.timestamp > this.ttl) {
this.store.delete(key);
return null;
}
return entry.value;
}
}
事件循环优化
耗时任务应分解:
// 阻塞事件循环
function syncTask() {
for(let i=0; i<1e8; i++) {
heavyCalculation(i);
}
}
// 优化方案
async function asyncTask() {
let i = 0;
const CHUNK_SIZE = 1e6;
function nextChunk() {
while(i < 1e8 && i % CHUNK_SIZE !== 0) {
heavyCalculation(i++);
}
if (i < 1e8) {
setImmediate(nextChunk);
}
}
nextChunk();
}
对象复用策略
频繁创建对象触发GC:
// 低效写法
function processRequest(req) {
const metrics = {
start: Date.now(),
method: req.method
};
// ...处理逻辑
return metrics;
}
改为对象池:
const metricPool = {
allocate() {
return this._pool.pop() || {
start: 0,
method: ''
};
},
free(obj) {
this._pool.push(obj);
},
_pool: []
};
function optimizedHandler(req) {
const metrics = metricPool.allocate();
metrics.start = Date.now();
metrics.method = req.method;
// ...处理逻辑
metricPool.free(metrics);
}
错误处理优化
避免重复实例化错误对象:
// 常规写法
app.get('/api', (req, res, next) => {
try {
validateInput(req.query);
} catch (err) {
next(new Error('验证失败')); // 每次新建Error实例
}
});
// 优化方案
const VALIDATION_ERROR = Object.freeze(
new Error('验证失败')
);
app.get('/api', (req, res, next) => {
try {
validateInput(req.query);
} catch (err) {
next(VALIDATION_ERROR); // 复用错误实例
}
});
依赖加载优化
动态加载非必要模块:
// 传统写法
const PDFGenerator = require('heavy-pdf-lib');
router.post('/report', async (req, res) => {
const pdf = new PDFGenerator();
// ...生成PDF
});
// 按需加载
router.post('/report', async (req, res) => {
const { default: PDFGenerator } = await import('heavy-pdf-lib');
const pdf = new PDFGenerator();
});
日志记录优化
同步日志影响性能:
// 问题代码
function logRequest(req) {
fs.appendFileSync('access.log', `${req.url}\n`);
}
改用WritableStream:
const { Writable } = require('stream');
const logStream = new Writable({
write(chunk, _, callback) {
fs.appendFile('access.log', chunk, callback);
}
});
function logOptimized(req) {
logStream.write(`${req.url}\n`);
}