• JavaScript – Fetch


    前言

    我几乎没有用过 Fetch. 第一次接触 Fetch 是 AngularJS (那年 es6 还没有普及). 但是 AngularJS 已经封装它了. 后来只是阅读了一些教程.

    发现它有一个致命缺点, 不支持 upload 进度. 于是就再也没有关注它了. 再后来用了 Angular 2 的 Http 到 Angular 4 的 HttpClient 都是上层封装.

    一直到最近看了一篇文章说 Fetch 比以前好一点了, 好像是支持了 abort, 但依然没有支持 upload 进度. 趁着我在复习 JavaScript 也一起整理它吧.

    参考

    阮一峰 – Fetch API 教程

    Fetch 的好处

    Fetch 是 es6 的产物, 它简化了以前 XMLHttpRequest 的使用方式. 可以算是 XMLHttpRequest 的替代品 (唯一无法替代的地方是, Fetch 不支持 upload 进度, 真的是唯一败笔啊)

    Fetch 有 3 个好处:

    1. 它基于 Promise. Promise 比 XMLHttpRequest.onreadystatechange 好太多了. 搭配 await 更理想.

    2 Request, Header, Response class, Fetch 用了 3 个主要的 class 来完成工作, 对比 XMLHttpRequest 就一个 class 太臃肿了

    3. 支持下载分段, 通过 body.getReader 可以一部分一部分的提前使用 response data. 而像 XMLHttpRequest 必须等所有 data 下载完了才可以开始用. 

    1 和 2 主要是在代码优化上面加分. 第 3 点则是具体的功能 (虽然很少项目需要).

    吐槽 XMLHttpRequest 

    下面这个是 XMLHttpRequest 的写法

    const xhttp = new XMLHttpRequest();
    xhttp.onreadystatechange = () => {
      if (xhttp.readyState === 4) {
        console.log(xhttp.status);
      }
    };
    xhttp.open('GET', 'https://localhost:7063/WeatherForecast', true);
    xhttp.send();

    下面这个是 Fetch 的写法

    const response = await fetch('https://localhost:7063/WeatherForecast');
    console.log(response.status);

    没有对比没有伤害, XMLHttpRequest 的调用接口设计糟糕透了...

    Send Request (GET)

    simplest

    先从最简单的 GET 说起

    fetch('https://localhost:7063/WeatherForecast')

    因为 fetch 默认的 method 是 GET, 所以只要给一个地址, 它就会发 GET 请求了.

    with query params

    query params 没有被 Fetch 封装, 直接用 URLSearchParams build 好后拼接到 URL 即可

    const queryParams = new URLSearchParams({
      param1: 'param1',
      param2: 'param2',
    });
    fetch(`https://localhost:7063/WeatherForecast?${queryParams.toString()}`);

    with headers

    fetch('https://localhost:7063/WeatherForecast', {
      headers: {
        Custom: 'custom header value',
        Accept: 'application/json; charset=utf-8',
      },
    });

    Fetch 的第二个参数可以配置所以的 options

    headers 属性的 interface 是

    下面这 2 种写法都是 ok 的

    with credentials

    允许同域发送 cookie

    fetch('https://localhost:7063/WeatherForecast', {
      credentials: 'same-origin',
    });

    with Request

    fetch('https://localhost:7063/WeatherForecast', {
      headers: {
        Accept: 'application/json; charset=utf-8',
      },
    });

    也可以先创建 Request 才传入 fetch

    const request = new Request('https://localhost:7063/WeatherForecast', {
      headers: {
        Accept: 'application/json; charset=utf-8',
      },
    });
    const response = await fetch(request);

    new Request 的参数和 fetch 是同样的. Request 的好处是可以 clone, 修改, 容易复用.

    Read Response

    response 分 2 part. 一个是 header info, status 这类. 它们是马上可以读取的 (同步)

    另一 part 是 body, 它是异步读取的. 而且可以分段式读.

    read status

    const response = await fetch('https://localhost:7063/WeatherForecast');
    const status = response.status; // 200;
    const succeed = response.ok; // true

    response.ok 是一个方便. 只要 status 是 200 – 299 都算 ok.

    read header

    使用 header 对象. 它提供了一些方便的接口, 比如 get, has, keys, values, entries 等等

    const response = await fetch('https://localhost:7063/WeatherForecast');
    const contentType = response.headers.get('Content-Type'); // application/json; charset=utf-8
    const customHeaderValue = response.headers.get('custom'); // 'a, b' 如果有 2 个会 combine by common + space

    重复 header?

    如果有重复的 header 那么它会合并起来

    结果是 'a, b'

    跨域 custom header

    服务端记得返回 expose header, 不然跨域情况下是拿不到 custom header 的哦

    read body text

    所以 body 都是异步读取的.

    const textContent = await response.text();

    如果返回的是 plain text 或者你想拿到 json string 就可以使用 .text()

    read body json

    const parsedJsonValue = await response.json();

    .json() 还会帮你把 json string 拿去 parse 哦.

    blob, arrayBuffer, formData

    还有 3 种 read from body, 比较少用到.

    blob 用在拿 image, files

    arrayBuffer 用在拿 audio, video

    formData 用在 Service Worker 拦截 formData post request

    multiple read body

    const response = await fetch('https://localhost:7063/WeatherForecast');
    const jsonText = await response.text();
    const jsonValue = await response.json();

    一个 response 不能被读取超过 1 次. 上面这样 .text 又 .json 是不 ok 的. 会报错

    解决方法是先 clone 一个 response 就可以了.

    const jsonText = await response.clone().text(); // 加了 clone
    const jsonValue = await response.json();

    body reader 分段式读取

    const response = await fetch('https://localhost:7063/WeatherForecast');
    const reader = response.body!.getReader();
    while (true) {
      const { done, value } = await reader.read();
      if (done) {
        break;
      }
      console.log(`Received ${value.length} bytes`);
    }

    下面是我 request 一个 mp4 的效果

    配上 header 的 Content-Length 配上 data.length 就可以显示下载进度了, 这种分段式读取是 XMLHttpRequest 做不到的.

    Send Request (POST)

    之前写过一篇 HTML – Native Form 原生表单功能集, 里面的 Ajax Form 有介绍了如何用 XMLHttpRequest send post request.

    Fetch 的逻辑也差不多

    发 JSON

    fetch('https://localhost:7063/WeatherForecast', {
      headers: {
        'Content-Type': 'application/json; charset=utf-8',
      },
      method: 'POST',
      body: JSON.stringify({ username: 'Derrick' }),
    });

    记得要添加 header Content-Type 哦.

    发 application/x-www-form-urlencoded 和 multipart/form-data

    fetch('https://localhost:7063/WeatherForecast', {
      method: 'POST',
      body: new URLSearchParams({ username: 'Derrick' }), // application/x-www-form-urlencoded
      body: new FormData(document.querySelector('form')!), // multipart/form-data
    });

    记得, 不要添加 header Content-Type, 游览器会自己添加.

    Abort Request

    abort 是用来中断请求的, 可能 user 按错, 或者后悔了. 

    通常只有那些很耗时的操作才需要设计 abort. 比如批量修改.

    当前端 abort 以后, 后端就好也是处理一下. 避免不必要的消耗.

    创建 abort controller

    const abortCtrl = new AbortController();

    把 signal 放入 fetch

    const request = new Request('https://localhost:7063/WeatherForecast', {
      signal: abortCtrl.signal
    });

    发送, 一秒后 abort

    await fetch(request);
    
    setTimeout(() => {
      abortCtrl.abort();
    }, 1000);

    前端监听 abort

    可以通过 signal 监听和查看当前 abort status

    abortCtrl.signal.addEventListener('abort', () => {
      console.log('abort done');
    });

    后端判断 abort

    ASP.NET Core 有一个 cancellationToken, 可以随时查看是否前端已经 abort 了 request, 如果已经 abort 那么就没有必要继续执行了, 直接返回就是了. 

  • 相关阅读:
    JavaScript基础知识
    java线程池
    Rop框架学习笔记
    Redis学习笔记之多机数据库
    【ML】Two-Stream Convolutional Networks for Action Recognition in Videos
    【CV】ICCV2015_Unsupervised Learning of Spatiotemporally Coherent Metrics
    【CV】ICCV2015_Describing Videos by Exploiting Temporal Structure
    【CV】CVPR2015_A Discriminative CNN Video Representation for Event Detection
    【ML】ICML2015_Unsupervised Learning of Video Representations using LSTMs
    【CV】ICCV2015_Unsupervised Visual Representation Learning by Context Prediction
  • 原文地址:https://www.cnblogs.com/keatkeat/p/16300650.html
Copyright © 2020-2023  润新知