异步编程的演进
Node.js 的核心就是异步。从最早的回调函数,到 Promise,再到现在的 async/await,异步编程的体验越来越好了。
回调时代
fs.readFile('config.json', (err, data) => {
if (err) throw err
db.connect(JSON.parse(data), (err, conn) => {
if (err) throw err
conn.query('SELECT * FROM users', (err, rows) => {
// 回调地狱...
})
})
})
Promise 时代
readFile('config.json')
.then(data => db.connect(JSON.parse(data)))
.then(conn => conn.query('SELECT * FROM users'))
.then(rows => console.log(rows))
.catch(err => console.error(err))
async/await 时代
async function getUsers() {
const data = await readFile('config.json')
const conn = await db.connect(JSON.parse(data))
const rows = await conn.query('SELECT * FROM users')
return rows
}
并发控制
当需要同时处理多个异步操作时,Promise.all 和 Promise.allSettled 是你的好朋友:
// 并发执行,全部成功
const [users, posts, comments] = await Promise.all([
fetchUsers(),
fetchPosts(),
fetchComments()
])
// 并发执行,容忍失败
const results = await Promise.allSettled([
fetchFromAPI1(),
fetchFromAPI2(),
fetchFromAPI3()
])
错误处理
永远不要忽略异步操作的错误,否则程序会在你意想不到的地方崩溃。
一个实用的包装函数:
function safe(promise) {
return promise
.then(data => [null, data])
.catch(err => [err, null])
}
const [err, data] = await safe(fetchData())
if (err) {
console.error('Failed:', err.message)
}
总结
- 优先使用 async/await
- 用 Promise.all 处理并发
- 永远处理错误
- 注意内存泄漏(未取消的定时器、未关闭的流)