yaofly2012 / note

Personal blog
https://github.com/yaofly2012/note/issues
44 stars 5 forks source link

WebAPI: fetch #187

Open yaofly2012 opened 4 years ago

yaofly2012 commented 4 years ago

一、fetch函数

1.1 语法

fetch(input: RequestInfo, init?: RequestInit): Promise<Response>

形参同Request

本质上也是通过实参创建Request实例,即:

var request = new Request('https://example.com', { method: 'POST'})
fetch(request);

等价于:

fetch('https://example.com', { method: 'POST'});

fetch返回值Promise被reject

Fetch_API里提到:

it will only reject on network failure or if anything prevented the request from completing.

即只有当网络请求不能完成时才会reject,如:

  1. 断网(Chrome Network面板切换到Offline试试);
  2. CORS预检失败

1.2 fetch & timeout

fetch没有超时配置。一般通过Promsie.race进行处理。

function timeoutPromise(ms) {
    return new Promise((resolve, reject) => {
        setTimeout(reject, ms)
    })
}

function fetchTimeout(input, init) {
    const { timeout = 3000, ...restInit } = init || {};
    return Promise.race(fetch(input, restInit), timeoutPromise(timeout));
}

也有利用Promise.then处理的:

function timeoutPromise(ms, promise) {
  return new Promise((resolve, reject) => {
    const timeoutId = setTimeout(() => {
      reject(new Error("promise timeout"))
    }, ms);
    promise.then(
      (res) => {
        clearTimeout(timeoutId);
        resolve(res);
      },
      (err) => {
        clearTimeout(timeoutId);
        reject(err);
      }
    );
  })
}

1.3 中断fetch请求

如何取消异步操作

1.4 PK XMLHttpRequest

MDN fetch API:

  1. but the new API provides a more powerful and flexible feature set.

  2. fetch返回的是个Promise,结果只有两个(fulfill, rejected), ajax细分了更多的错误:
    • onerror
    • ontimeout
    • onabort
  3. fetch没有超时API,而XMLHttpRequest

好吧已经讨论了 Fetch 永生 Why I still use XHR instead of the Fetch API MDN Using Fetch也在讨论了

1.5 APIs

  1. fetch函数只是Fetch 系列 API之一。
  2. RequestResponse不只是只能应用在fetch,他们比较一般化,可以应用在其他处理Request, Response的场景(如SW里所有的请求和响应都是RequestResponse)。

参考:

  1. MDN Fetch_API
  2. MDN Using_Fetch
yaofly2012 commented 4 years ago

二、Request

Request表示一个Http请求,从HTTP角度看一个请求格式是:

  1. 起始行:method url HTTP/版本
  2. 请求Headers
  3. 请求Body

Request则是表示这三块信息的对象,并添加了一些扩展信息,可以看下RequestInit

2.1 Request.mode

控制请求模式,有4个取值:

默认值

2.2 Request.credentials

控制请求是否携带Cookie, basic http auth,主要针对跨域请求。有3个取值:

默认值: 浏览器默认值是same-origin, 但在旧版本浏览器,例如safari 11依旧是omit,safari 12已更改。

yaofly2012 commented 4 years ago

Headers

存储RequestResponse的Header数据,并提供相关的操作API(增,删,改,查)。

  1. name-value集合;
  2. Headers本质也就是key-value对,但是key是大小写不敏感的。

Apis

  1. 没有单独的add方法,因为添加header有三种方式
    • 增加新header
    • 对已存在的header,追加新的value(比如Accept header)
    • 对已存在的header,覆盖新的value。

所以单独的add方法并不能表达具体的添加方式,最终使用语言更明确的Headers.append()Headers.set()方法添加header。

  1. Headers存储是有序的,按照字典字母升序排列(统一转成小写格式排序)。
  2. 看到Headers.keys()容易联想到Object.keys,但是Headers.keys()返回的是iterator,不是数组。

PK 对象

在使用fetch或者直接调用Header构造函数时,一般直接传递个初始化对象,而不是调用append或则set方法。

fetch('http://exmaple.com', {
  headers: {
    test: 'yao'
  }
})

var headers = new Headers({
  test: 'yao'
})

为啥不简单点直接用一般的对象代表Headers,而是单独使用HeaderAPI?

Headers对象并没有那么简单:

  1. 大小写不敏感处理(对象的属性是大小写敏感的);
  2. 丰富的属性的操作API;
  3. Headername/value字符串集比对象的key/value字符集较窄。并且对应非法的name会抛异常:
    // Failed to construct 'Headers': Invalid name
    var headers = new Headers({
                            '{': 'hello'
                        })
  4. Headers对象具有Guard特性。

什么是Guard ?

  1. A Headers object also has an associated guard

  2. Guard is a feature of Headers objects

影响set(), append(), delete()方法的行为。

Guard处理过程:

Guard是内部自动控制,无法手动修改。

  1. 创建Headers对象时,Guardnone,此时可以任意操作Headers
  2. 当创建了Request或者Response对象时,关联的Headersguard取值: image

    • request 不能修改禁止修改Request Headers

      var headers = new Headers({
                          'x-test': 'yaoq',
                          'A': 'A',
                          'z-upper': 'z',
                          '1': '1'
                      })
                  // append1
                  headers.append('Accept', 'application/json')
                  for(var key of headers.keys(headers)) {
                      console.log(key)
                  }
      
                  var req = new Request(url, {
                      // mode: 'no-cors',
                      // credentials: 'same-origin',
                      headers: headers
                  })            
                  // append2
                  headers.append('x-test2', 'text/html')
      
                  for(var key of headers.keys(headers)) {
                      console.log(key)
                  }

      但是append2处依旧可以添加新的header,只是最终的Request请求里并没有x-test2header。

分析Headers.append逻辑,理解guard

image