Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WebAPI: fetch #187

Open
yaofly2012 opened this issue Oct 16, 2020 · 2 comments
Open

WebAPI: fetch #187

yaofly2012 opened this issue Oct 16, 2020 · 2 comments

Comments

@yaofly2012
Copy link
Owner

yaofly2012 commented Oct 16, 2020

一、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
  1. fetch没有超时API,而XMLHttpRequest

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

1.5 APIs

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

参考:

  1. MDN Fetch_API
  2. MDN Using_Fetch
@yaofly2012
Copy link
Owner Author

yaofly2012 commented Oct 18, 2020

二、Request

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

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

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

2.1 Request.mode

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

  • same-origin 只能同源请求
    如果跨域请求则报错:

Fetch API cannot load http://127.0.0.1:8090/fetch. Request mode is "same-origin" but the URL's origin is not same as the request origin http://127.0.0.1:8080.

  • no-cors 简单请求可以跨域请求
    对于非简单的跨域请求,且请求准许跨域,此时Response.type="opaque",JS无法访问Response的属性,否则会报错。
  • cors 可以跨域请求
  • navigate A navigate request is created only while navigating between documents.
    Request不只是服务fetchfetch函数估计用不到。

默认值

  • 手动通过new Request()构造(fetch内部也是这种方式),则默认值为cors,其他创建方式(例如,对与嵌入资源发起的请求)都是no-cors

2.2 Request.credentials

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

  • omit:不携带Cookie,即使同源请求也不携带;
    同[XMLHttpRequest.withCredentials](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/withCredentials)=true
  • same-origin: 同源才携带Cookie;
  • include: 不管是否同源,都携带Cookiebasic http auth
    类似[XMLHttpRequest.withCredentials](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/withCredentials)=true

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

@yaofly2012
Copy link
Owner Author

yaofly2012 commented Oct 18, 2020

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'
                        })
  1. 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
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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant