《深入浅出 Node.js》第四章:异步编程 详细总结

第四章是第三章的"续集"和"解药"。第三章告诉你"异步为什么牛、底层怎么实现的",第四章告诉你"异步这么难写,怎么才能优雅地驾驭它"。朴灵作者把焦点从底层原理转向实践编程模型,系统介绍了从原始回调到现代 async/await 的解决方案演进路径。

这一章的核心思想:控制权反转------把"下一步干什么"交给框架/机制,而不是手动嵌套回调。

我们按小节逐一详细总结,每个细节配解释 + 多例子 + 生动比喻。

4.1 异步编程的优势与难点(第三章3.4已讲,这里略过回顾)

优势:高并发、低资源。

难点:回调地狱、异常难捕获、流程复杂。

生动比喻:回调地狱像"俄罗斯套娃",一层套一层;像"金字塔",越写越斜。

4.2 事件发布/订阅模型(EventEmitter)

核心思想

"一对多"解耦:发布者只管emit事件,订阅者只管on监听,互不干扰。

生动比喻:像广播电台(发布者)喊"新闻来了!",所有收音机(订阅者)都能收到,谁想听就调频。

API 详解

js 复制代码
const EventEmitter = require('events');
const ee = new EventEmitter();

// 订阅
ee.on('news', (data) => console.log('收到新闻:', data));     // 多次触发
ee.once('alert', () => console.log('紧急警报,只响一次'));  // 只一次

// 发布
ee.emit('news', '股市大涨');
ee.emit('news', '天气预报');
ee.emit('alert');  // 只打印一次

// 移除
const handler = () => console.log('移除我');
ee.on('remove', handler);
ee.removeListener('remove', handler);

例子1:文件处理多监听

js 复制代码
class FileProcessor extends EventEmitter {}

const processor = new FileProcessor();

processor.on('read', (data) => console.log('日志记录:', data.length));
processor.on('read', (data) => console.log('缓存更新'));
processor.on('read', (data) => console.log('响应客户端'));

processor.emit('read', bigBuffer);  // 三个监听都执行

例子2:错误约定

js 复制代码
ee.on('error', (err) => console.error('出错了:', err));  // 必须监听!
ee.emit('error', new Error('boom'));  // 不监听会崩溃进程

优势:解耦、灵活、可组合(Stream、http、net底层都用它)。

4.3 Promise/Deferred模式

核心思想

把嵌套回调变成链式,错误统一catch。

生动比喻:回调地狱是"横着爬山",Promise是"顺着绳子往下跳",一步接一步。

Promise 三状态

Pending → Fulfilled / Rejected(不可逆)

当时Deferred模式(第三方库如Q/Bluebird)

js 复制代码
function readFile(filename) {
  const deferred = Q.defer();
  fs.readFile(filename, (err, data) => {
    err ? deferred.reject(err) : deferred.resolve(data);
  });
  return deferred.promise;
}

链式使用

js 复制代码
readFile('a.txt')
  .then(data => {
    console.log('a:', data);
    return readFile('b.txt');  // 返回新Promise,继续链
  })
  .then(data => {
    console.log('b:', data);
    throw new Error('出错');
  })
  .catch(err => console.error('统一捕获:', err));  // 任何一层错误都到这里

例子1:并行Promise.all

js 复制代码
Promise.all([readFile('a'), readFile('b'), readFile('c')])
  .then(([a, b, c]) => console.log('三个文件都读完'))
  .catch(err => console.error('任何一个失败就进来'));

例子2:值穿透

js 复制代码
Promise.resolve(1)
  .then(() => '字符串')          // 返回非Promise,直接传下一个
  .then(str => console.log(str)); // '字符串'

优势:扁平化、错误冒泡、并行控制。

4.4 流程控制库

4.4.1 尾触发与nextTick

同步尾触发会栈溢出,异步用nextTick推到队列尾。

例子

js 复制代码
function tail() {
  process.nextTick(() => tail());  // 不会栈溢出
}

4.4.2 async库(最经典,至今流行)

  • series:串行
  • parallel:并行
  • waterfall:串行且传值
  • auto:复杂依赖

例子1:waterfall串行传值

js 复制代码
async.waterfall([
  cb => fs.readFile('a.txt', cb),
  (dataA, cb) => {
    console.log(dataA);
    fs.readFile('b.txt', cb);
  },
  (dataB, cb) => cb(null, '最终结果')
], (err, result) => console.log(result));

例子2:parallel并行

js 复制代码
async.parallel([
  cb => fs.readFile('a.txt', cb),
  cb => fs.readFile('b.txt', cb)
], (err, [a, b]) => console.log(a, b));

4.4.3 Step库(线性写法)

js 复制代码
Step(
  function() { fs.readFile('a.txt', this); },
  function(err, data) {
    console.log(data);
    fs.readFile('b.txt', this);
  }
);

4.4.4 Wind库(编译器风格,接近async/await)

js 复制代码
var read = eval(Wind.compile("async", function() {
  var a = $await(fs.readFileAsync('a.txt'));
  var b = $await(fs.readFileAsync('b.txt'));
  console.log(a, b);
}));
read().start();

4.5 现代补充:async/await(终极形态)

所有方案的集大成:

js 复制代码
async function main() {
  try {
    const a = await readFile('a.txt');
    const b = await readFile('b.txt');
    console.log(a, b);
  } catch (err) {
    console.error(err);  // 统一捕获
  }
}
main();

生动比喻

  • 回调:横着爬山(地狱)
  • EventEmitter:广播喊人
  • Promise:顺绳子跳(链式)
  • async/await:直接走平路(像同步)

总结与收获

第四章告诉你:异步难写,但有层层递进的解决方案。从事件解耦,到Promise链式,再到流程库控制,最后async/await让代码重回"同步美感"。

相关推荐
David凉宸2 小时前
Vue 3生态系统深度解析与最佳实践
前端·javascript·vue.js
全栈小52 小时前
【前端】win11操作系统安装完最新版本的NodeJs运行npm install报错,提示在此系统上禁止运行脚本
前端·npm·node.js
晚霞的不甘2 小时前
Flutter for OpenHarmony3D DNA 螺旋可视化:用 Canvas 构建沉浸式分子模型
前端·数据库·经验分享·flutter·3d·前端框架
a11177611 小时前
医院挂号预约系统(开源 Fastapi+vue2)
前端·vue.js·python·html5·fastapi
0思必得011 小时前
[Web自动化] Selenium处理iframe和frame
前端·爬虫·python·selenium·自动化·web自动化
行走的陀螺仪13 小时前
uni-app + Vue3编辑页/新增页面给列表页传参
前端·vue.js·uni-app
We་ct14 小时前
LeetCode 205. 同构字符串:解题思路+代码优化全解析
前端·算法·leetcode·typescript
2301_8127314114 小时前
CSS3笔记
前端·笔记·css3
ziblog14 小时前
CSS3白云飘动动画特效
前端·css·css3
越努力越幸运50814 小时前
CSS3学习之网格布局grid
前端·学习·css3