130

浅谈 Fetch

 6 years ago
source link: https://juejin.im/post/5a1ffb00f265da431b6cfe00
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Fetch

参考: developers.google.com/web/updates…

现在可能还有一些很旧的程序还在使用XHR,类似下面的写法:

const request = new XMLHttpRequest()
request.responseType = 'json'
request.open('GET', '/url', true)
request.onload = () => {
  console.log(request.response)
}
request.onerror = () => {
  console.log('shits happen!')
}
request.send(null)
复制代码

这样子使用XHR进行异步访问、读取资源显得很繁琐,相对比Fetch()允许你创建类似XHR的network访问,但是使用更简单而且干净的API,不需要多次回调并且记住XHR复杂的API。Fetch API底层是通过Promises实现。

XMLHttpRequest

一个相对完整的XMLHttpRequest至少需要监听两个事件(onload、onerror)来实现成功和失败的回调,以及调用open()和send()

function reqListener() {
  var data = JSON.parse(this.responseText);
  console.log(data);
}

function reqError(err) {
  console.log('Fetch Error :-S', err);
}

var oReq = new XMLHttpRequest();
oReq.onload = reqListener;
oReq.onerror = reqError;
oReq.open('get', './api/some.json', true);
oReq.send();
复制代码

Fetch

一个简单的Fetch例子如下:

fetch('./api/some.json')
  .then(
    function(response) {
      if (response.status !== 200) {
        console.log('Looks like there was a problem. Status Code: ' +
          response.status);
        return;
      }

      // Examine the text in the response
      response.json().then(function(data) {
        console.log(data);
      });
    }
  )
  .catch(function(err) {
    console.log('Fetch Error :-S', err);
  });
复制代码

Fetch的语法更加语义化、比较好理解。在上面的例子里我们先判断response的status码,如果是200我们才将response解析为JSON

fetch()请求返回的response是Stream对象,因此我们调用response.json时由于异步读取流对象所以返回的是一个Promise对象。

Fetch用async优化代码

由于Fetch底层是用Promise实现,我们可以直接用async来优化上面的代码,减少回调,使其更加语义化、容易理解

async function geturl(){
	try{
		let res = await fetch('./api/some.json')
		if(res.status == 200){
			console.log(await res.text())
		}
	} catch(err){
		console.log(err)
	}
}
复制代码

Response元数据

在上面的例子里,我们了解了Response对象的status状态以及怎么把response对象转换为JSON对象,让我们来看看Response对象的其他元数据:

fetch('users.json').then(function(response) {
    console.log(response.headers.get('Content-Type'));
    console.log(response.headers.get('Date'));

    console.log(response.status);
    console.log(response.statusText);
    console.log(response.type);
    console.log(response.url);
});
复制代码

Response类型

当我们发起一个Fetch请求时,返回的response响应会自带一个response.type属性(basic、cors、opaque)。response.type属性说明了异步资源的来源,同时还有相应的处理方式。

当我们发起一个同源请求时,response.type为basic,而且你可以从response读取全部信息。

如果我们访问一个非同源域名,并且有返回相应的CORs响应头时,那么该请求类型是cors。cors和basic很相似,就除了cors响应里你无法访问Cache-ControlContent-LanguageContent-TypeExpiresLast-ModifiedPragma

当我们对一个不同源的域名发起请求时,如果返回的响应头部没有CORS信息,那么这个response对应的类型就是opaque类型。一个opaque响应是无法读取返回的数据、状态,甚至无法确定这个请求是否成功。

我们可以自定义Fetch请求的模式,要求返回对应类型的响应,有以下几种响应:

  1. same-origin 只返回同源请求,其他类型会被reject
  2. cors 接收同源、非同源请求,返回有CORs头部的响应
  3. cors-with-forced-preflight 在发出请求前会先做一次安全性检查
  4. no-cors 用来发起没有CORS头部并且非同源请求,并且会返回opaque响应。但是目前这种类型只能在Service Worker里使用,在window.fetch里不能用
fetch('http://some-site.com/cors-enabled/some.json', {mode: 'cors'})
  .then(function(response) {
    return response.text();
  })
  .then(function(text) {
    console.log('Request successful', text);
  })
  .catch(function(error) {
    log('Request failed', error)
  });
复制代码

因为Fetch返回的response是基于Promise实现,所以我们可以像链条一样把几个Promise串接起来,如下所示:

function status(response) {
  if (response.status >= 200 && response.status < 300) {
    return Promise.resolve(response)
  } else {
    return Promise.reject(new Error(response.statusText))
  }
}

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

fetch('users.json')
  .then(status)
  .then(json)
  .then(function(data) {
    console.log('Request succeeded with JSON response', data);
  }).catch(function(error) {
    console.log('Request failed', error);
  });
复制代码

当然了,我们也可以用async进行代码优化

async function geturl(url){
	try {
		let res = await fetch(url)
		if(res.status >= 200 && res.status < 300){
			console.log('Request succeeded with JSON response', await res.json())
		}
	}catch (err){
		console.log(err)
	}
}

geturl('users.json')
复制代码

Post请求

当我们使用Fetch发起Post请求时,需要手动设置method参数和body参数,如下:

fetch(url, {
    method: 'post',
    headers: {
      "Content-type": "application/x-www-form-urlencoded; charset=UTF-8"
    },
    body: 'foo=bar&lorem=ipsum'
  })
  .then(json)
  .then(function (data) {
    console.log('Request succeeded with JSON response', data);
  })
  .catch(function (error) {
    console.log('Request failed', error);
  });
复制代码

如果没有显式指定method参数,那么默认Get请求

带Cookie发送请求

如果我们想要在异步请求中带上cookie参数,那么需要显式指定credentials参数:

fetch(url, {
  credentials: 'include'
})
复制代码

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK