Skip to content
On this page

Node.js 异步编程

Node.js是单线程的,通过异步编程来处理高并发请求。本章详细介绍Node.js中的异步编程模式。

回调函数

回调函数是Node.js中最基本的异步处理方式。

基本回调函数

javascript
const fs = require('fs');

// 异步读取文件
fs.readFile('./data.txt', 'utf8', (err, data) => {
  if (err) {
    console.error('读取文件失败:', err);
    return;
  }
  console.log('文件内容:', data);
});

console.log('这行会先执行'); // 这行会先执行

回调地狱问题

javascript
// 回调地狱示例
getData(function(a) {
  getMoreData(a, function(b) {
    getEvenMoreData(b, function(c) {
      getEvenEvenMoreData(c, function(d) {
        // 嵌套过深,难以维护
        console.log(d);
      });
    });
  });
});

错误优先回调

Node.js中的异步操作通常使用错误优先回调模式:

javascript
function asyncOperation(param, callback) {
  // 模拟异步操作
  setTimeout(() => {
    if (param) {
      callback(null, `处理成功: ${param}`); // 第一个参数为null表示无错误
    } else {
      callback(new Error('参数无效')); // 第一个参数为错误对象
    }
  }, 1000);
}

asyncOperation('test', (err, result) => {
  if (err) {
    console.error('错误:', err.message);
  } else {
    console.log('结果:', result);
  }
});

Promise

Promise是解决回调地狱的一种方式,提供了更好的错误处理和链式调用。

创建Promise

javascript
function asyncOperation(param) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (param) {
        resolve(`处理成功: ${param}`);
      } else {
        reject(new Error('参数无效'));
      }
    }, 1000);
  });
}

// 使用Promise
asyncOperation('test')
  .then(result => {
    console.log('结果:', result);
    return asyncOperation('chained');
  })
  .then(result => {
    console.log('链式结果:', result);
  })
  .catch(err => {
    console.error('错误:', err.message);
  });

Promise常用方法

javascript
const fs = require('fs').promises;
const util = require('util');

// 将回调函数转换为Promise
const readFileAsync = util.promisify(fs.readFile);

// Promise.all - 并行执行所有Promise
Promise.all([
  asyncOperation('task1'),
  asyncOperation('task2'),
  asyncOperation('task3')
])
.then(results => {
  console.log('所有任务完成:', results);
})
.catch(err => {
  console.error('其中一个任务失败:', err.message);
});

// Promise.race - 返回第一个完成的Promise
Promise.race([
  asyncOperation('fast'),
  asyncOperation('slow')
])
.then(result => {
  console.log('最快完成:', result);
});

// Promise.allSettled - 等待所有Promise完成(无论成功或失败)
Promise.allSettled([
  asyncOperation('success'),
  Promise.reject(new Error('失败'))
])
.then(results => {
  results.forEach((result, index) => {
    if (result.status === 'fulfilled') {
      console.log(`任务${index}成功:`, result.value);
    } else {
      console.log(`任务${index}失败:`, result.reason.message);
    }
  });
});

async/await

async/await是基于Promise的语法糖,使异步代码看起来像同步代码。

基本用法

javascript
async function asyncFunction() {
  try {
    const result1 = await asyncOperation('task1');
    console.log('任务1结果:', result1);
    
    const result2 = await asyncOperation('task2');
    console.log('任务2结果:', result2);
    
    return '所有任务完成';
  } catch (err) {
    console.error('错误:', err.message);
    throw err;
  }
}

// 调用async函数
asyncFunction()
  .then(result => {
    console.log('最终结果:', result);
  })
  .catch(err => {
    console.error('最终错误:', err.message);
  });

并行执行async/await

javascript
async function parallelExecution() {
  try {
    // 并行执行
    const [result1, result2, result3] = await Promise.all([
      asyncOperation('task1'),
      asyncOperation('task2'),
      asyncOperation('task3')
    ]);
    
    console.log('并行结果:', result1, result2, result3);
  } catch (err) {
    console.error('并行执行错误:', err.message);
  }
}

// 或者先启动所有异步操作,再等待结果
async function parallelExecution2() {
  try {
    // 启动所有异步操作
    const promise1 = asyncOperation('task1');
    const promise2 = asyncOperation('task2');
    const promise3 = asyncOperation('task3');
    
    // 等待所有操作完成
    const result1 = await promise1;
    const result2 = await promise2;
    const result3 = await promise3;
    
    console.log('并行结果:', result1, result2, result3);
  } catch (err) {
    console.error('并行执行错误:', err.message);
  }
}

事件驱动异步编程

Node.js的事件驱动模型也是异步编程的重要组成部分。

javascript
const EventEmitter = require('events');

class AsyncProcessor extends EventEmitter {
  async process(data) {
    this.emit('start', data);
    
    try {
      const result = await asyncOperation(data);
      this.emit('success', result);
      return result;
    } catch (err) {
      this.emit('error', err);
      throw err;
    } finally {
      this.emit('end');
    }
  }
}

const processor = new AsyncProcessor();

processor.on('start', (data) => {
  console.log('开始处理:', data);
});

processor.on('success', (result) => {
  console.log('处理成功:', result);
});

processor.on('error', (err) => {
  console.error('处理失败:', err.message);
});

processor.on('end', () => {
  console.log('处理结束');
});

// 使用
processor.process('test data');

流(Streams)中的异步操作

流是Node.js中处理数据的异步方式:

javascript
const fs = require('fs');
const { Transform } = require('stream');

// 创建转换流
const upperCaseStream = new Transform({
  transform(chunk, encoding, callback) {
    // 异步处理每个数据块
    setImmediate(() => {
      const upperChunk = chunk.toString().toUpperCase();
      callback(null, upperChunk);
    });
  }
});

// 使用流进行异步处理
const readStream = fs.createReadStream('./input.txt');
const writeStream = fs.createWriteStream('./output.txt');

readStream
  .pipe(upperCaseStream)
  .pipe(writeStream)
  .on('finish', () => {
    console.log('流处理完成');
  });

异步操作最佳实践

1. 错误处理

javascript
// 不好的做法
async function badExample() {
  const result = await someAsyncOperation(); // 没有错误处理
  return result;
}

// 好的做法
async function goodExample() {
  try {
    const result = await someAsyncOperation();
    return result;
  } catch (err) {
    console.error('异步操作失败:', err);
    throw new Error(`操作失败: ${err.message}`);
  }
}

2. 避免阻塞

javascript
// 不好的做法 - 阻塞事件循环
async function badLoop() {
  const items = Array.from({length: 1000000}, (_, i) => i);
  
  for (const item of items) {
    // 执行大量同步操作,会阻塞事件循环
    heavySyncOperation(item);
  }
}

// 好的做法 - 分批处理,给其他任务执行机会
async function goodLoop() {
  const items = Array.from({length: 1000000}, (_, i) => i);
  
  for (let i = 0; i < items.length; i++) {
    heavySyncOperation(items[i]);
    
    // 每处理1000个元素后让出控制权
    if (i % 1000 === 0) {
      await new Promise(resolve => setImmediate(resolve));
    }
  }
}

3. 合理使用并发控制

javascript
// 控制并发数量
async function controlledConcurrency(tasks, concurrency = 3) {
  const results = [];
  const executing = [];
  
  for (const [index, task] of tasks.entries()) {
    const promise = task().then(result => {
      results[index] = result;
    });
    
    executing.push(promise);
    
    if (executing.length >= concurrency) {
      await Promise.race(executing);
      executing.splice(executing.findIndex(p => p === promise), 1);
    }
  }
  
  await Promise.all(executing);
  return results;
}

4. 异步函数中的性能优化

javascript
// 数据库查询示例
class DatabaseService {
  async getUserWithPosts(userId) {
    // 并行执行独立的查询
    const [user, posts] = await Promise.all([
      this.getUser(userId),
      this.getPostsByUserId(userId)
    ]);
    
    return { user, posts };
  }
  
  async getBatchUsers(userIds) {
    // 批量查询优化
    const users = await Promise.all(
      userIds.map(id => this.getUser(id))
    );
    
    return users;
  }
}

异步编程模式对比

模式优点缺点适用场景
回调函数简单直观容易形成回调地狱简单的异步操作
Promise链式调用,错误处理统一语法相对复杂需要链式处理的场景
async/await代码清晰,类似同步写法需要Promise基础复杂的异步逻辑

Node.js的异步编程是其高性能的关键,掌握这些异步编程模式对于开发高质量的Node.js应用至关重要。