Node.js 异步编程最佳实践

异步编程的演进

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.allPromise.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 处理并发
  • 永远处理错误
  • 注意内存泄漏(未取消的定时器、未关闭的流)