使用装饰器模式强大你的 fetch

简介: 使用装饰器模式强大你的 fetch

1. fetch() 很好,但你可能希望更好

fetch() API允许你在web应用程序中执行网络请求。

fetch()的用法非常简单:调用fetch ('/movies.json')来启动请求。当请求完成时,您将获得一个Response对象,从中提取数据。

下面是一个简单的例子,如何从movies.json URL获取JSON格式数据:

async function executeRequest() {
  const response = await fetch('/movies.json');
  const moviesJson = await response.json();
  console.log(moviesJson);
}
executeRequest(); 
// logs [{ name: 'Heat' }, { name: 'Alien' }]

如上面的代码片段所示,必须手动从响应中提取JSON对象:moviesJson = await response.JSON()。只做一次,没问题。但是如果您的应用程序执行许多请求,那么使用await response.json()提取JSON对象的所有时间是非常繁琐的。

因此,通常使用第三方库,比如axios,它可以极大地简化请求的处理。考虑使用axios获取相同的电影:

async function executeRequest() {
  const moviesJson = await axios('/movies.json');
  console.log(moviesJson);
}
executeRequest(); 
// logs [{ name: 'Heat' }, { name: 'Alien' }]

moviesJson = await axios('/movies.json')返回实际的JSON响应。不必像fetch()所要求的那样手动提取JSON

但是,使用像axios这样的辅助库也会带来一些问题

首先,它增加了web应用程序的bundle大小。其次,您的应用程序与第三方库相结合:您获得了该库的所有好处,但也得到了它的所有bug

我的目的是采用一种不同的方法,从这两个方面都得到了最好的结果——使用装饰器模式来增加fetch() API的易用性和灵活性

其思想是将一个基fetch类(我将展示如何定义它)包装为您需要的任何其他功能:提取JSON、超时、在糟糕的HTTP状态下抛出错误、处理auth头,等等。让我们在下一节中看看如何做到这一点。

2. 准备 Fetcher 接口

装饰器模式非常有用,因为它支持以灵活和松散耦合的方式在基本逻辑之上添加功能(换句话说——装饰)。

如果你不熟悉装饰模式,我建议您阅读它是如何工作的。

应用装饰器来增强fetch()需要几个简单的步骤:

  • 第一步是声明一个名为Fetcher的抽象接口:
type ResponseWithData = Response & { data?: any };
interface Fetcher {
  run(
    input: RequestInfo, 
    init?: RequestInit
  ): Promise<ResponseWithData>;
} 

Fetcher接口只有一个方法,它接受相同的参数并返回与常规fetch()相同的数据类型。

  • 第二步是实现基本的fetcher类:
class BasicFetcher implements Fetcher {
  run(
    input: RequestInfo, 
    init?: RequestInit
  ): Promise<ResponseWithData> {
    return fetch(input, init);
  }
}

BasicFetcher实现了Fetcher接口。它的一个方法run()调用常规的fetch()函数。

例如,让我们使用基本的fetcher类来获取电影列表:

const fetcher = new BasicFetcher();
const decoratedFetch = fetcher.run.bind(fetcher);
async function executeRequest() {
  const response = await decoratedFetch('/movies.json');
  const moviesJson = await response.json();
  console.log(moviesJson);
}
executeRequest(); 
// logs [{ name: 'Heat' }, { name: 'Alien' }]

const fetcher = new BasicFetcher()创建一个fetcher类的实例。decoratedFetch = fetcher.run.bind(fetcher)创建一个绑定方法。

然后你可以使用decoratedFetch('/movies.JSON ')来获取电影JSON,就像使用常规的fetch()一样。

在这一步,BasicFetcher类没有带来好处。此外,由于新接口和新类的出现,事情变得更加复杂!稍等片刻,你会发现当装饰者模式被引入到行动中时所带来的巨大好处。

3. 给提取 JSON 数据的方法添加装饰器

装饰器模式的主要是装饰器类。

装饰器类必须符合Fetcher接口,包装被装饰的实例,以及在run()方法中引入额外的功能

让我们实现一个从响应对象中提取JSON数据的装饰器:

class JsonFetcherDecorator implements Fetcher {
  private decoratee: Fetcher;
  constructor (decoratee: Fetcher) {
    this.decoratee = decoratee;
  }
  async run(
    input: RequestInfo, 
    init?: RequestInit
  ): Promise<ResponseWithData> {
    const response = await this.decoratee.run(input, init);
    const json = await response.json();
    response.data = json;
    return response;
  }
}

让我们仔细看看JsonFetcherDecorator是如何构造的。

JsonFetcherDecorator符合Fetcher接口。

JsonExtractorFetch有一个私有字段decoratee,它也符合Fetcher接口。在run()方法中this.decoratee.run(input, init)执行实际的数据获取。

然后json = await response.json()从响应中提取json数据。最后,响应。data = json将提取的json数据分配给响应对象。

现在让我们用JsonFetcherDecorator装饰器来组合装饰BasicFetcher,并简化fetch()的使用:

const fetcher = new JsonFetcherDecorator(
  new BasicFetcher()
);
const decoratedFetch = fetcher.run.bind(fetcher);
async function executeRequest() {
  const { data } = await decoratedFetch('/movies.json');
  console.log(data);
}
executeRequest(); 
// logs [{ name: 'Heat' }, { name: 'Alien' }]

现在,您可以从响应对象的data属性访问所提取的数据,而不是从响应中手动提取JSON数据。

通过将JSON提取器移动到装饰器,现在在任何使用const {data} = decoratedFetch(URL)的地方,你都不必手动提取JSON对象。

4. 创建请求超时装饰器

默认情况下,fetch() API会在浏览器指定的时间超时。在Chrome中,网络请求超时时间为300秒,而在Firefox中超时时间为90秒。

用户可以等待8秒来完成简单的请求。这就是为什么需要为网络请求设置一个超时,并在8秒后通知用户网络问题的原因。

装饰器模式的伟大之处在于,可以使用任意多的装饰器装饰你的基本实现!那么,让我们为取回请求创建一个超时装饰器:

const TIMEOUT = 8000; // 8 seconds
class TimeoutFetcherDecorator implements Fetcher {
  private decoratee: Fetcher;
  constructor(decoratee: Fetcher) {
    this.decoratee = decoratee;
  }
  async run(
    input: RequestInfo, 
    init?: RequestInit
  ): Promise<ResponseWithData> {
    const controller = new AbortController();
    const id = setTimeout(() => controller.abort(), TIMEOUT);
    const response = await this.decoratee.run(input, {
      ...init,
      signal: controller.signal
    });
    clearTimeout(id);
    return response;
  }
}

TimeoutFetcherDecorator是一个实现Fetcher接口的decorator

TimeoutFetcherDecoratorrun()方法内部:如果请求在8秒内没有完成,则使用中止控制器中止请求。

现在让我们来使用这个装饰器:

const fetcher = new TimeoutFetcherDecorator(
  new JsonFetcherDecorator(
    new BasicFetcher()
  )
);
const decoratedFetch = fetcher.run.bind(fetcher);
async function executeRequest() {
  try {
    const { data } = await decoratedFetch('/movies.json');
    console.log(data);
  } catch (error) {
    // Timeouts if the request takes
    // longer than 8 seconds
    console.log(error.name);
  }
}
executeRequest(); 
// if the request takes more than 8 seconds
// logs "AbortError"

在这个示例中,对/movies.json的请求需要超过8秒。

decoratedFetch('/movies.json'),由于TimeoutFetcherDecorator,抛出超时错误。

现在基本的获取器被封装在2个装饰器中:一个提取JSON对象,另一个在8秒内超时请求。这极大地简化了decoratedFetch()的使用:当调用decoratedFetch()时,decorator逻辑将为你工作。

5. 总结

fetch() API提供了执行获取请求的基本功能。但你需要的不止这些。单独使用fetch()强制你手动从请求中提取JSON数据,配置超时,等等。

为了避免样板文件,你可以使用更友好的库,如axios。然而,使用像axios这样的第三方库会增加应用包的大小,同时你也会与之紧密结合。

另一种解决方案是在fetch()上面应用装饰器模式。您可以创建从请求中提取JSON、超时请求等等的装饰器。你可以随时组合、添加或删除装饰器,而不会影响使用装饰器的代码。


相关文章
|
1天前
|
云安全 人工智能 运维
阿里云SecOps Agent,全新安全跨产品执行体验
自然语言驱动 云安全中心/WAF/CFW/ 等多款安全产品联动
1572 1
|
12天前
|
缓存 测试技术 API
Qwen 3.7 Plus 与 Max 实测:性价比与多模态能力差异解析(2026)
2026 年 6 月 1 日,阿里悄无声息地发布了 Qwen 3.7 Plus,距 Qwen 3.7 Max 上线刚好 11 天。同样的 1M 上下文,同样的 35 小时自治上限。但价格才是头条:Plus 是 0.40/M输入,Max是 2.50/M——便宜约 6 倍——并且还能看图、看视频。Vision Arena 上 Plus 已经排到 #16。所以这周真正值得讨论的问题不是”要不要为视觉能力买单”,而是”Max 凭什么用 6 倍价格换来 2 个百分点的 benchmark 领先”。
|
13天前
|
JavaScript 定位技术 API
CodeGraph 爆火:编程 Agent 需要的不是更多上下文,而是一张提前画好的代码地图
CodeGraph 是一款爆火的本地代码智能工具,通过 tree-sitter 解析 AST 构建结构化知识图谱(存于 SQLite),为编程 Agent 提前生成“代码地图”。它显著降低 Agent 在中大型项目中的探索成本——实测工具调用减少71%、Token 降57%、速度提升46%,支持19+语言及主流框架路由识别,完全离线、无需 API Key。
856 11
CodeGraph 爆火:编程 Agent 需要的不是更多上下文,而是一张提前画好的代码地图
|
13天前
|
人工智能 运维 JavaScript
阿里云Qoder CN(原通义灵码)全解析 产品形态、版本划分与技术适配说明
在AI辅助开发与智能办公工具持续普及的当下,阿里云旗下原通义灵码正式更名为Qoder CN,同时延伸出QoderWork CN、Qoder CN CLI、Qoder CN Mobile等多款配套产品,形成覆盖代码开发、日常办公、终端交互、移动端使用的完整工具矩阵。Qoder CN核心定位为AI智能编码助手,深度适配主流代码编辑器、集成开发环境以及终端场景;QoderWork CN则偏向桌面端综合办公辅助,二者面向不同使用场景,划分了多个版本档位,搭配差异化资源配额、功能权限与计费规则,同时兼容多款主流大模型。
895 8
|
1天前
|
机器学习/深度学习 人工智能 调度
🐴 HappyHorse 1.1 现已上线阿里云百炼!快来查收模型使用指南,现在调用享 6 折~
HappyHorse 1.1 是新一代视频生成大模型,全面升级动态表现力、角色一致性、指令遵循、视觉质感与音画协同能力。支持I2V/T2V/R2V三类生成,适配短剧、电商广告、品牌营销等场景,提供高质、流畅、可控的AI视频生产力。
395 2
🐴 HappyHorse 1.1 现已上线阿里云百炼!快来查收模型使用指南,现在调用享 6 折~
|
13天前
|
JSON 缓存 安全
通过 CC Switch 本地路由让 Codex CLI 接入 DeepSeek 等第三方模型
CC Switch 通过本地路由(`127.0.0.1:15721`)实现协议转换:将 Codex 的 Responses API 请求自动映射为 DeepSeek 等厂商的 Chat Completions 接口,兼容流式响应与工具调用,无需修改 Codex 源码,安全隔离 API Key。(239字)
2467 7
通过 CC Switch 本地路由让 Codex CLI 接入 DeepSeek 等第三方模型
|
13天前
|
存储 安全 Java
AgentScope Java 2.0:打造分布式、企业级智能体底座
AgentScope 2.0 面向分布式部署、稳定运行、权限安全等企业级需求全面升级,打造支持多租户隔离与长期稳定运行的企业级智能体底座。
|
8天前
|
人工智能 自然语言处理 算法
阿里云百炼Qwen 3.7 Plus与Max实测全解:性价比与多模态能力、成本深度对比
2026年,阿里云百炼平台推出的Qwen 3.7系列成为企业与开发者落地AI应用的核心选择,其中Qwen 3.7 Max与Plus作为两大旗舰版本,定位差异显著:Max是纯文本推理旗舰,专注高强度智能体与复杂逻辑任务;Plus则是多模态全能版,在保留强大文本能力的同时,补齐图像、视频理解能力,且价格大幅降低。本文基于2026年最新实测数据,从核心参数、文本能力、多模态能力、智能体表现、性价比与场景选型六大维度,全面解析两款模型的差异,为用户提供精准选型参考。
442 0