首页
技术小册
AIGC
面试刷题
技术文章
MAGENTO
云计算
视频课程
源码下载
PDF书籍
「涨薪秘籍」
登录
注册
01 | 函数式vs.面向对象:响应未知和不确定
02 | 如何通过闭包对象管理程序中状态的变化?
03 | 如何通过部分应用和柯里化让函数具象化?
04 | 如何通过组合、管道和reducer让函数抽象化?
05|map、reduce和monad如何围绕值进行操作?
06 | 如何通过模块化、异步和观察做到动态加载?
07 | 深入理解对象的私有和静态属性
08|深入理解继承、Delegation和组合
09|面向对象:通过词法作用域和调用点理解this绑定
10|JS有哪8种数据类型,你需要注意什么?
11|通过JS引擎的堆栈了解闭包原理
12|JS语义分析该用迭代还是递归?
13 | JS引擎如何实现数组的稳定排序?
14 | 通过SparkPlug深入了解调用栈
15 | 如何通过哈希查找JS对象内存地址?
16 | 为什么环形队列适合做Node数据流缓存?
17 | 如何通过链表做LRU/LFU缓存?
18 | TurboFan如何用图做JS编译优化?
19 | 通过树和图看如何在无序中找到路径和秩序
20 | 算法思想:JS中分治、贪心、回溯和动态规划
21 | 创建型:为什么说Redux可以替代单例状态管理
22|结构型:Vue.js如何通过代理实现响应式编程
23 | 结构型:通过jQuery看结构型模式
24 | 行为型:通过观察者、迭代器模式看JS异步回调
25 | 行为型:模版、策略和状态模式有什么区别?
26|特殊型:前端有哪些处理加载和渲染的特殊“模式”?
27|性能:如何理解JavaScript中的并行、并发?
28|性能:通过Orinoco、Jank Busters看垃圾回收
29|网络:从HTTP/1到HTTP/3,你都需要了解什么?
30|安全:JS代码和程序都需要注意哪些安全问题?
31|测试(一):开发到重构中的测试
32|测试(二):功能性测试
33|测试(三):非功能性测试
34|静态类型检查:ESLint语法规则和代码风格的检查
35|Flow:通过Flow类看JS的类型检查
36|包管理和分发:通过NPM做包的管理和分发
37|编译和打包:通过Webpack、Babel做编译和打包
38|语法扩展:通过JSX来做语法扩展
39|Polyfill:通过Polyfill让浏览器提供原生支持
40|微前端:从MVC贫血模式到DDD充血模式
41|大前端:通过一云多端搭建跨PC/移动的平台应用
42|元编程:通过Proxies和Reflect赋能元编程
当前位置:
首页>>
技术小册>>
JavaScript进阶实战
小册名称:JavaScript进阶实战
### 24 | 行为型:通过观察者、迭代器模式看JS异步回调 在JavaScript的广阔世界里,异步编程是不可或缺的一部分,它让JavaScript能够处理耗时的操作(如网络请求、文件读写等)而不阻塞主线程,从而保持应用的响应性和流畅性。然而,异步编程也带来了其特有的挑战,如回调地狱(Callback Hell)、状态管理复杂等问题。幸运的是,设计模式——尤其是行为型模式中的观察者(Observer)和迭代器(Iterator)模式,为我们提供了一种优雅地处理异步回调和复杂状态管理的方式。本章节将深入探讨这两种模式在JavaScript异步编程中的应用。 #### 一、观察者模式与异步事件处理 观察者模式是一种行为型设计模式,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。在JavaScript中,这种机制广泛应用于事件监听和异步回调处理中。 ##### 1.1 JavaScript中的事件监听 JavaScript的事件监听机制本质上就是观察者模式的一种实现。当我们在DOM元素上添加事件监听器时,实际上是让该元素(主题对象)在特定事件发生时,通知所有注册了该事件的回调函数(观察者)。例如: ```javascript const button = document.getElementById('myButton'); // 定义观察者函数 function onClickHandler() { console.log('Button was clicked!'); } // 注册观察者 button.addEventListener('click', onClickHandler); // 当按钮被点击时,主题对象(button)通知所有观察者(包括onClickHandler) ``` ##### 1.2 自定义观察者模式实现异步回调 除了DOM事件,我们还可以在JavaScript中自定义实现观察者模式来处理异步回调。例如,创建一个消息中心,用于管理不同组件间的通信: ```javascript class MessageCenter { constructor() { this.observers = {}; } subscribe(eventType, callback) { if (!this.observers[eventType]) { this.observers[eventType] = []; } this.observers[eventType].push(callback); } unsubscribe(eventType, callback) { if (this.observers[eventType]) { this.observers[eventType] = this.observers[eventType].filter(obs => obs !== callback); } } notify(eventType, ...args) { if (this.observers[eventType]) { this.observers[eventType].forEach(obs => obs(...args)); } } } // 使用示例 const messageCenter = new MessageCenter(); function handleMessage(message) { console.log(`Received message: ${message}`); } messageCenter.subscribe('newMessage', handleMessage); // 假设在某个异步操作中 setTimeout(() => { messageCenter.notify('newMessage', 'Hello, World!'); }, 1000); ``` #### 二、迭代器模式与异步数据处理 迭代器模式是一种行为型设计模式,它提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露该对象的内部表示。在JavaScript中,迭代器模式通常与`for...of`循环、`Symbol.iterator`等特性结合使用,以支持对集合的遍历。然而,迭代器模式在异步数据处理中同样有着独特的应用场景,尤其是在处理异步流(如异步生成器)时。 ##### 2.1 异步迭代器与`for...await...of` ES2018引入了异步迭代器(Async Iterator)和`for...await...of`循环,允许我们以同步的方式编写异步遍历逻辑。异步迭代器是遵循迭代器协议的对象,但它返回的是Promise对象,而不是直接的值。 ```javascript async function* asyncGenerator() { const urls = ['https://api.example.com/data1', 'https://api.example.com/data2']; for (const url of urls) { const response = await fetch(url); const data = await response.json(); yield data; // 产出Promise解析后的数据 } } async function processData() { for await (const data of asyncGenerator()) { console.log(data); // 同步风格处理异步数据 } } processData(); ``` 在这个例子中,`asyncGenerator`是一个异步生成器函数,它逐个请求URL并产出JSON数据。`processData`函数则使用`for...await...of`循环来遍历这些数据,以同步的方式处理每个异步请求的结果。 ##### 2.2 自定义异步迭代器处理复杂异步流 除了使用内置的异步生成器,我们还可以自定义异步迭代器来处理更复杂的异步流。例如,实现一个能够自动重试失败的请求的异步迭代器: ```javascript async function* retryIterator(urls, maxRetries = 3) { for (const url of urls) { let retries = 0; while (retries < maxRetries) { try { const response = await fetch(url); if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`); const data = await response.json(); yield data; break; // 成功获取数据后跳出循环 } catch (error) { retries++; console.error(`Failed to fetch ${url}, retrying... (${retries}/${maxRetries})`); if (retries >= maxRetries) { throw error; // 达到最大重试次数后抛出错误 } await new Promise(resolve => setTimeout(resolve, 1000)); // 等待一秒后重试 } } } } // 使用自定义异步迭代器 async function processDataWithRetries() { for await (const data of retryIterator(['https://api.example.com/unstable'])) { console.log(data); } } processDataWithRetries().catch(console.error); ``` 在这个例子中,`retryIterator`是一个自定义的异步迭代器,它会对每个URL进行多次尝试,直到成功获取数据或达到最大重试次数。这种机制在处理可能不稳定的API或网络请求时非常有用。 #### 总结 通过本章节的探讨,我们看到了观察者模式和迭代器模式在JavaScript异步编程中的强大应用。观察者模式让我们能够以松耦合的方式处理异步事件和回调,而迭代器模式(特别是异步迭代器)则提供了一种优雅的方式来遍历和处理异步数据流。这些设计模式不仅提高了代码的可读性和可维护性,还使得异步编程变得更加直观和易于管理。在未来的JavaScript开发中,深入理解和灵活运用这些设计模式,将有助于我们编写出更加高效、健壮和可扩展的异步代码。
上一篇:
23 | 结构型:通过jQuery看结构型模式
下一篇:
25 | 行为型:模版、策略和状态模式有什么区别?
该分类下的相关小册推荐:
零基础学JavaScript
JavaScript面试指南
KnockoutJS入门指南
Javascript-ES6与异步编程
npm script实战构建前端工作流
深入学习前端重构知识体系
Javascript重点难点实例精讲(一)
web前端开发性能优化实战
Javascript编程指南
ES6入门指南
经典设计模式Javascript版
编程入门课:Javascript从入门到实战