乐趣区

关于前端:用装饰器模式增强-fetch

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 程序的包大小;其次,你的程序与第三方库联合在了一起,不论是益处还是 bug。

所以就想到了第三种办法——用装璜器模式来进步 fetch() API 的易用性和灵活性。

筹备 Fetcher 接口

装璜器模式十分弱小,它可能以灵便和涣散耦合的形式在根本逻辑之上增加性能(也就是所谓的“装璜”)。

应用装璜器来加强 fetch() 须要几个简略的步骤。

第一步是申明一个名为 Fetcher 的形象接口:

type ResponseWithData = Response & {data?: any};

interface Fetcher {
  run(
    input: RequestInfo, 
    init?: RequestInit
  ): Promise<ResponseWithData>;
} 

Fetcher 接口只有一个办法,该办法承受与一般 fetch() 雷同的参数并返回雷同类型的数据。

第二步是实现根本的访存器类:

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 类不会带来任何益处。而且因为引入了新的接口和类,使代码变得更加简单了。

JSON 提取装璜器

装璜器模式的外围是装璜器类。

装璜器类必须合乎 Fetcher 接口,包装装璜后的实例,并在 run() 办法中引入其余性能。

上面实现一个装璜器,它从 response 对象中提取 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,ini) 获取数据。

而后 json = await response.json() 从响应中提取 JSON 数据。最初,response.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'}]

当初就能够间接从 response 对象的 data 属性拜访提取的数据,在每个应用 const {data} = decoratedFetch(URL) 的中央,都不用手动提取 JSON 对象了。

申请超时装璜器

默认状况下,fetch() API 的超时工夫受到浏览器的制约。在 Chrome 中,网络申请超时为 300 秒,而在 Firefox 中为 90 秒。

假如咱们最多能够期待 8 秒能力实现简略的申请,这时须要设置网络申请超时工夫,并在 8。秒后告诉用户无关网络问题的信息。

装璜器模式的长处在于:能够依据须要应用任意数量的装璜器来装璜根本实现。接下来再为 fetch 申请创立一个超时装璜器:

const TIMEOUT = 8000; // 8 秒超时

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 接口的装璜器。

如果申请没有在 8 秒内实现,那么在 TimeoutFetcherDecorator.run() 办法中的 controller.abort() 会停止申请。

测试一下:

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) {
    // 如果申请超过 8 秒
    console.log(error.name);
  }
}

executeRequest(); 
// 如果申请耗时超过 8 秒则输入“AbortError”

因为应用了 TimeoutFetcherDecoratordecoratedFetch('/movies.json') 会引发超时谬误。

当初 fetch() 被 2 个装璜器包装:一个装璜器用于提取 JSON 对象,另一个在 8 秒内使申请超时。这就极大地简化了 DecoratedFetch() 的应用。

总结

fetch()API 提供了申请的基本功能。然而只用 fetch() 会强制你从申请中手动提取 JSON 数据,本人去配置超时等等。

只管咱们能够应用第三方库,然而应用 axios 之类的库会减少程序包的大小,并减少耦合度。

通过应用装璜器模式,能够使装璜器从申请中提取 JSON,使申请超时等等。另外还能够随时合并、增加或删除装璜器,而不会影响应用了带装璜器的 fetch() 的其余代码。


本文首发微信公众号:前端先锋

欢送扫描二维码关注公众号,每天都给你推送陈腐的前端技术文章


欢送持续浏览本专栏其它高赞文章:

  • 深刻了解 Shadow DOM v1
  • 一步步教你用 WebVR 实现虚拟现实游戏
  • 13 个帮你进步开发效率的古代 CSS 框架
  • 疾速上手 BootstrapVue
  • JavaScript 引擎是如何工作的?从调用栈到 Promise 你须要晓得的所有
  • WebSocket 实战:在 Node 和 React 之间进行实时通信
  • 对于 Git 的 20 个面试题
  • 深刻解析 Node.js 的 console.log
  • Node.js 到底是什么?
  • 30 分钟用 Node.js 构建一个 API 服务器
  • Javascript 的对象拷贝
  • 程序员 30 岁前月薪达不到 30K,该何去何从
  • 14 个最好的 JavaScript 数据可视化库
  • 8 个给前端的顶级 VS Code 扩大插件
  • Node.js 多线程齐全指南
  • 把 HTML 转成 PDF 的 4 个计划及实现

  • 更多文章 …
退出移动版