Fetch 常见的使用问题

▶fetch的浏览器兼容


㈠fetch默认不携带cookie

⑴fetch发送请求默认是不发送cookie的,不管是同域还是跨域;

  需要设置 :

fetch(url, {credentials: 'include'})

可以配置其credentials项,其有3个值:

omit: 默认值,忽略cookie的发送

same-origin: 表示cookie只能同域发送,不能跨域发送

include: cookie既可以同域发送,也可以跨域发送

⑵fetch默认对服务端通过Set-Cookie头设置的cookie也会忽略,若想选择接受来自服务端的cookie信息,也必须要配置credentials选项

㈡fetch请求对某些错误http状态不会reject

⑴这主要是由fetch返回promise导致的,因为fetch返回的promise在某些错误的http状态下如400、500不会reject,相反它会被resolve

⑵只有网络错误会导致请求不能完成时,fetch 才会被 reject;所以一般会对fetch请求做一层封装

⑶示例:

function checkStatus(response) {
 
  if (response.status >= 200 && response.status < 300) {
    
         return response;
  }
  
  const error = new Error(response.statusText);
  
   error.response = response;
  
   throw error;
}

  function parseJSON(response) {
  
      return response.json();
}

  export default function request(url, options) {
  
   let opt = options||{};
  
   return fetch(url, {credentials: 'include', ...opt})
    
       .then(checkStatus)
   
       .then(parseJSON)
    
       .then((data) => ( data ))
  
       .catch((err) => ( err ));

㈢fetch不支持超时timeout处理

⑴fetch不像大多数ajax库那样对请求设置超时timeout,它没有有关请求超时的feature,所以在fetch标准添加超时feature之前,都需要polyfill该特性。

⑵实际上,我们真正需要的是abort(), timeout可以通过timeout+abort方式来实现,起到真正超时丢弃当前的请求。

⑶而在目前的fetch指导规范中,fetch并不是一个具体实例,而只是一个方法

⑷其返回的promise实例根据Promise指导规范标准是不能abort的,也不能手动改变promise实例的状态,只能由内部来根据请求结果改变promise的状态

⑸因此:

实现fetch的timeout功能,其思想就是新创建一个可以手动控制promise状态的实例

根据不同情况来对新promise实例进行resolve或者reject,从而达到实现timeout的功能;

⑹目前可以有两种不同的解决方法:

第一种:单纯setTimeout方式

var oldFetchfn = fetch; //拦截原始的fetch方法

window.fetch = function(input, opts){//定义新的fetch方法,封装原有的fetch方法
   
    return new Promise(function(resolve, reject){
        
       var timeoutId = setTimeout(function(){
            
           reject(new Error("fetch timeout"))
       
       }, opts.timeout);
        
         oldFetchfn(input, opts).then(
            res=>{
                clearTimeout(timeoutId);
                resolve(res)
            },
            err=>{
                clearTimeout(timeoutId);
                reject(err)
            }
        )
    })
}

在上面基础上可以模拟类似XHR的abort功能:

var oldFetchfn = fetch; 

window.fetch = function(input, opts){
    
   return new Promise(function(resolve, reject){
        
      var abort_promise = function(){
            
           reject(new Error("fetch abort"))
        };
       
       var p = oldFetchfn(input, opts).then(resolve, reject);
       
        p.abort = abort_promise;
        
        return p;
    })
}

②第二种方法:利用Promise.race方法

Promise.race方法接受一个promise实例数组参数,表示多个promise实例中任何一个最先改变状态,那么race方法返回的promise实例状态就跟着改变。

var oldFetchfn = fetch; //拦截原始的fetch方法

window.fetch = function(input, opts){//定义新的fetch方法,封装原有的fetch方法
    
     var fetchPromise = oldFetchfn(input, opts);
    
     var timeoutPromise = new Promise(function(resolve, reject){
       
        setTimeout(()=>{
             
            reject(new Error("fetch timeout"))
        
      }, opts.timeout)
   
   });
    
     retrun Promise.race([fetchPromise, timeoutPromise])
}

⑺timeout不是请求连接超时的含义,它表示请求的response时间,包括请求的连接、服务器处理及服务器响应回来的时间

⑻fetch的timeout即使超时发生了,本次请求也不会被abort丢弃掉,它在后台仍然会发送到服务器端只是本次请求的响应内容被丢弃而已

㈣fetch不支持JSONP

⑴fetch是与服务器端进行异步交互的,而JSONP是外链一个javascript资源,并不是真正ajax,所以fetch与JSONP没有什么直接关联,当然至少目前是不支持JSONP的。

⑵我们要实现一个JSONP,只不过这个JSONP的实现要与fetch的实现类似,即基于Promise来实现一个JSONP;而其外在表现给人感觉是fetch支持JSONP一样;

⑶JSONP的实现步骤如下:

①首先需要用npm安装fetch-jsonp

npm install fetch-jsonp --save-dev

②然后在像下面一样使用:

fetchJsonp('/users.jsonp', {
    timeout: 3000,
    jsonpCallback: 'custom_callback'
  })
  .then(function(response) {
    return response.json()
  }).catch(function(ex) {
    console.log('parsing failed', ex)
  })

㈤fetch不支持progress事件

XHR是原生支持progress事件的

⑵但是呢,根据fetch的指导规范标准,其内部设计实现了RequestResponse类;

   其中Response封装一些方法和属性,通过Response实例可以访问这些方法和属性:

⑶例如response.json()response.body等等;

⑷response.body是一个可读字节流对象,其实现了一个getRender()方法,其具体作用是:

getRender()方法用于读取响应的原始字节流,该字节流是可以循环读取的,直至body内容传输完成

㈥fetch跨域问题

⑴XHR2是支持跨域请求的,只不过要满足浏览器端支持CORS,服务器通过Access-Control-Allow-Origin来允许指定的源进行跨域,仅此一种方式。

⑵fetch也是支持跨域请求的,只不过其跨域请求做法与XHR2一样,需要客户端与服务端支持

⑶fetch还支持一种跨域,不需要服务器支持的形式,具体可以通过其mode的配置项来说明。

⑷fetch的mode配置项有3个值:

same-origin该模式是不允许跨域的,它需要遵守同源策略,否则浏览器会返回一个error告知不能跨域;其对应的response type为basic

cors: 该模式支持跨域请求,顾名思义它是以CORS的形式跨域;当然该模式也可以同域请求不需要后端额外的CORS支持;其对应的response type为cors

no-cors: 该模式用于跨域请求但是服务器不带CORS响应头,也就是服务端不支持CORS;这也是fetch的特殊跨域请求方式;其对应的response type为opaque

⑸针对跨域请求,cors模式是常见跨域请求实现,但是fetch自带的no-cors跨域请求模式则较为陌生,该模式有一个比较明显的特点:

该模式允许浏览器发送本次跨域请求,但是不能访问响应返回的内容,这也是其response type为opaque透明的原因。

⑹这与<img/>发送的请求类似,只是该模式不能访问响应的内容信息;但是它可以被其他APIs进行处理,例如ServiceWorker。

⑺另外,该模式返回的repsonse可以在Cache API中被存储起来以便后续的对它的使用,这点对script、css和图片的CDN资源是非常合适的,

   因为这些资源响应头中都没有CORS头

总的来说,fetch的跨域请求是使用CORS方式,需要浏览器和服务端的支持。

㈦不可以取消

1.fetch不支持abort,不支持超时控制,使用setTimeout及Promise.reject的实现的超时控制并不能阻止请求过程继续在后台运行,造成了流量的浪费。

2.fetch没有办法原生监测请求的进度,而XHR可以

参考:https://www.cnblogs.com/huilixieqi/p/6494380.html

原文地址:https://www.cnblogs.com/shihaiying/p/12115281.html