利用 async/await 结合事件循环机制优化服务性能
- 理解事件循环机制:Node.js 基于 Chrome V8 引擎,其事件循环负责处理非阻塞 I/O 操作。事件循环不断检查事件队列,执行回调函数。在事件循环的每个阶段,Node.js 会处理特定类型的任务,如定时器到期的任务、I/O 回调等。
- async/await 如何配合事件循环:
async
函数返回一个 Promise。await
关键字只能在 async
函数内部使用,它会暂停 async
函数的执行,等待 Promise 被解决(resolved)或被拒绝(rejected)。
- 当
await
一个 Promise 时,控制权会交还给事件循环,允许其他任务在这个等待期间执行。这样就避免了阻塞主线程,使 Node.js 能够处理高并发的网络请求。
- 例如,假设我们有一个异步读取文件的操作:
async function readFileAsync() {
const fs = require('fs');
const util = require('util');
const readFile = util.promisify(fs.readFile);
try {
const data = await readFile('example.txt', 'utf8');
console.log(data);
} catch (error) {
console.error(error);
}
}
readFileAsync();
- 在这个例子中,
await readFile('example.txt', 'utf8')
会暂停 readFileAsync
函数的执行,事件循环可以继续处理其他任务。当文件读取完成(Promise 被 resolved),await
之后的代码才会执行。
处理大量网络请求时防止内存泄漏和提高资源利用率
- 合理使用异步操作避免内存堆积:在处理大量网络请求时,每个请求可能涉及多个异步操作,如数据库查询、文件读取等。如果这些操作不是异步执行,而是同步阻塞的,会导致内存中堆积大量未完成的请求,最终可能导致内存泄漏。
- 例如,处理 HTTP 请求的 Express 应用:
const express = require('express');
const app = express();
const mysql = require('mysql');
const pool = mysql.createPool({
host: 'localhost',
user: 'root',
password: 'password',
database: 'test'
});
const query = util.promisify(pool.query).bind(pool);
app.get('/data', async (req, res) => {
try {
const results = await query('SELECT * FROM users');
res.json(results);
} catch (error) {
res.status(500).send('Error fetching data');
}
});
const port = 3000;
app.listen(port, () => {
console.log(`Server running on port ${port}`);
});
- 在这个 Express 应用中,
await query('SELECT * FROM users')
确保数据库查询是异步的。这样,在等待数据库响应时,Node.js 可以处理其他 HTTP 请求,避免因大量请求等待数据库查询而导致的内存泄漏。
- 及时释放资源:在异步操作完成后,要确保及时释放相关资源。例如,在使用数据库连接池时,连接使用完毕后要归还到连接池。在上述代码中,
util.promisify(pool.query)
内部会自动处理连接的获取和归还,确保连接资源的有效利用。
- 流处理:对于处理大量数据(如文件上传、大查询结果集),使用流(Stream)结合
async/await
可以显著提高资源利用率。流允许数据逐块处理,而不是一次性加载到内存中。
const express = require('express');
const multer = require('multer');
const app = express();
const storage = multer.memoryStorage();
const upload = multer({ storage: storage });
app.post('/upload', upload.single('file'), async (req, res) => {
const fs = require('fs');
const util = require('util');
const writeFile = util.promisify(fs.writeFile);
try {
await writeFile('uploadedFile.txt', req.file.buffer);
res.send('File uploaded successfully');
} catch (error) {
res.status(500).send('Error uploading file');
}
});
const port = 3000;
app.listen(port, () => {
console.log(`Server running on port ${port}`);
});
- 在这个文件上传的例子中,
multer
将上传的文件存储在内存中作为 buffer
,然后通过 await writeFile('uploadedFile.txt', req.file.buffer)
异步写入文件,避免了在内存中长时间保留大量数据,提高了资源利用率。