什么是Fetch

2016-11-24 12:40:46来源:CSDN作者:crystal6918人点击

Fetch 是浏览器提供的原生 AJAX 接口。

由于原来的XMLHttpRequest不符合关注分离原则,且基于事件的模型在处理异步上已经没有现代的Promise等那么有优势。因此Fetch出现来解决这种问题。

Fetch API 提供了能够用于操作一部分 HTTP 的 JavaScript 接口,比如 requests 和 responses。它同时也提供了一个全局的 fetch() 方法——能够简单的异步的获取资源。
使用 window.fetch 函数可以代替以前的 $. ajax、$.get 和 $.post。

Ajax与Fetch

JavaScript 通过XMLHttpRequest(XHR)来执行异步请求,这个方式已经存在了很长一段时间。虽说它很有用,但它不是最佳API。
它在设计上不符合职责分离原则,将输入、输出和用事件来跟踪的状态混杂在一个对象里。
而且,基于事件的模型与最近JavaScript流行的Promise以及基于生成器的异步编程模型不太搭(事件模型在处理异步上有点过时了)。

var xhttp = new XMLHttpRequest();xhttp.onreadystatechange = function() {    if (this.readyState === 4 && this.status === 200) {      console.log(this.responseText);    }};xhttp.open("GET", "/", true);xhr.onload = function() {  console.log(xhr.response);};xhr.onerror = function() {  console.log("Oops, error");};xhttp.send();

而新的 Fetch API打算修正上面提到的那些缺陷。
Fetch API 提供了能够用于操作一部分 HTTP 的 JavaScript 接口,比如 requests 和 responses。它同时也提供了一个全局的 fetch() 方法——能够简单的异步的获取资源。

Fetch接口

Fetch 提供了对 Request 和 Response 等对象通用的定义。
发送请求或者获取资源,需要使用 fetch() 方法。

Headers

Fetch API 的Headers类允许你去对HTTP request和response headers执行各种操作。 这些操作包括:检索, 设置, 添加和删除。
一个Headers类里包含一个header列表, 它的初始值为空或者是零个或多个键值对。

下面是一个示例:

// 创建一个空的 Headers 对象,注意是Headers,不是Headervar headers = new Headers();// 添加(append)请求头信息headers.append('Content-Type', 'text/plain');headers.append('X-My-Custom-Header', 'CustomValue');// 判断(has), 获取(get), 以及修改(set)请求头的值headers.has('Content-Type'); // trueheaders.get('Content-Type'); // "text/plain"headers.set('Content-Type', 'application/json');// 删除某条请求头信息(a header)headers.delete('X-My-Custom-Header');// 创建对象时设置初始化信息var headers = new Headers({    'Content-Type': 'text/plain',    'X-My-Custom-Header': 'CustomValue'});

由于Headers可以在request请求中被发送或者在response请求中被接收,并且规定了哪些参数是可写的,Headers对象有一个特殊的guard属性。这个属性没有暴露给Web,但是它影响到哪些内容可以在Headers对象中被改变。

一般来说,需要创建一个 Request 对象来包装请求头:

var request = new Request('/some-url', {    headers: new Headers({        'Content-Type': 'text/plain'    })});fetch(request).then(function() { /* handle response */ });

Request

Request 对象表示一次 fetch 调用的请求信息。
可以使用构造函数Request.Request()来创建一个新的Request对象。

var req = new Request("/index.html");console.log(req.method); // "GET"console.log(req.url); // "http://example.com/index.html"

你也可以将一个建好的Request对象传给构造函数,这样将复制出一个新的Request:

var copy = new Request(req);console.log(copy.method); // "GET"console.log(copy.url); // "http://example.com/index.html"

URL以外的其他属性的初始值能够通过第二个参数传给Request构造函数。这个参数是一个json:

var uploadReq = new Request("/uploadImage", {  method: "POST",  headers: {    "Content-Type": "image/png",  },  body: "image data"});

Request对象的mode属性用来决定是否允许跨域请求,以及哪些response属性可读。可选的mode属性值为same-origin,no-cors(默认)以及cors。

  • same-origin:模式很简单,如果一个请求是跨域的,那么返回一个简单的error,这样确保所有的请求遵守同源策略。
  • no-cors:允许来自CDN的脚本、其他域的图片和其他一些跨域资源,但是首先有个前提条件,就是请求的method只能是”HEAD”,”GET”或者”POST”。
  • cors:我们通常用作跨域请求来从第三方提供的API获取数据。这个模式遵守CORS协议。只有有限的一些headers被暴露给Response对象,但是body是可读的。

Request对象的credentials枚举属性决定了cookies是否能跨域得到。

Response

Fetch API 的Response接口呈现了对一次请求的响应数据。
你可以使用Response.Response() 构造函数来创建一个Response对象,但一般更可能遇到的情况是,其他的API操作返回了一个Response对象,例如一个简单的 GlobalFetch.fetch()。因此Response实例通常在fetch()的回调中获得。
Response有一系列的属性:

  • Response.type 只读:包含Response的类型 (例如, basic, cors).
  • Response.url 只读:包含Response的URL.
  • Response.useFinalURL:包含了一个布尔值来标示这是否是该Response的最终URL.
  • Response.status 只读:包含Response的状态码 (例如, 200 成功).
  • Response.ok 只读:包含了一个布尔值来标示该Response成功(状态码200-299) 还是失败.
  • Response.statusText 只读:包含了与该Response状态码一致的状态信息 (例如, OK对应200).
  • Response.headers 只读:包含此Response所关联的Headers 对象.

处理body

无论Request还是Response都可能带着body。我们可以对其中的body进行处理。
Request和Response都为他们的body提供了以下方法,这些方法都返回一个Promise对象。

  • Body.arrayBuffer():读取 Response对象并且将它设置为已读(因为Responses对象被设置为了 stream 的方式,所以它们只能被读取一次) ,并返回一个被解析为ArrayBuffer格式的promise对象
  • Body.blob()读取 Response对象并且将它设置为已读(因为Responses对象被设置为了 stream 的方式,所以它们只能被读取一次) ,并返回一个被解析为Blob格式的promise对象
  • Body.formData():读取 Response对象并且将它设置为已读(因为Responses对象被设置为了 stream 的方式,所以它们只能被读取一次) ,并返回一个被解析为FormData格式的promise对象
  • Body.json():读取 Response对象并且将它设置为已读(因为Responses对象被设置为了 stream 的方式,所以它们只能被读取一次) ,并返回一个被解析为JSON格式的promise对象
  • Body.text():读取 Response对象并且将它设置为已读(因为Responses对象被设置为了 stream 的方式,所以它们只能被读取一次) ,并返回一个被解析为USVString格式的promise对象
fetch('https://davidwalsh.name/demo/arsenal.json').then(function(response) {     // 转换为 JSON    return response.json();}).then(function(j) {    // 现在, `j` 是一个 JavaScript object    console.log(j); });

非常重要的一点要说明,那就是Request和Response的body只能被读取一次!它们有一个属性叫bodyUsed,读取一次之后设置为true,就不能再读取了。

var res = new Response("one time use");console.log(res.bodyUsed); // falseres.text().then(function(v) {  console.log(res.bodyUsed); // true});console.log(res.bodyUsed); // trueres.text().catch(function(e) {  console.log("Tried to read already consumed Response");});

但有时候,我们希望多次访问body,API提供了一个clone()方法。调用这个方法可以得到一个克隆对象。不过要记得,clone()必须要在读取之前调用,也就是先clone()再读取。

var image1 = document.querySelector('.img1');var image2 = document.querySelector('.img2');var myRequest = new Request('flowers.jpg');fetch(myRequest).then(function(response) {  var response2 = response.clone();  response.blob().then(function(myBlob) {    var objectURL = URL.createObjectURL(myBlob);    image1.src = objectURL;  });  response2.blob().then(function(myBlob) {    var objectURL = URL.createObjectURL(myBlob);    image2.src = objectURL;  });});

fetch()

Fetch API 中的GlobalFetch 接口包含了GlobalFetch.fetch() 方法,它被用于发送请求获取资源。

// url (必须), options (可选)fetch('/some/url', {    method: 'get'}).then(function(response) {}).catch(function(err) {    // 出错了;等价于 then 的第二个参数,但这样更好用更直观 :(});

fetch API 也使用了 JavaScript Promises 来处理结果/回调:

// 链式处理,将异步变为类似单线程的写法: 高级用法.fetch('/some/url').then(function(response) {    return //... 执行成功, 第1步...}).then(function(returnedValue) {    // ... 执行成功, 第2步...}).catch(function(err) {    // 中途任何地方出错...在此处理 :( });

Fetch用法

浏览器支持情况

Fetch 的支持目前还处于早期的阶段,不过正在取得良好的进展。它目前在 Firefox 39 以上,和 Chrome 42 以上都被支持了。

如果你现在就想使用它,还可以用 Fetch Polyfil,用于支持那些还未支持 Fetch 的浏览器。

功能检测

Fetch API 的支持情况,可以通过检测 Headers、Request、Response 或 fetch() 是否在 Window 或 Worker 域中。

if(self.fetch) {    // run my fetch request here} else {    // do something with XMLHttpRequest?}

发起fetch请求

var myImage = document.querySelector("img");fetch("flowers.jpg").then(function(response){    return response.blob();}).then(function(myBlob){    var objectURL = URL.createObjectURL(myBlob);    myImage.src = objectURL;})

以上代码中,我们通过网络获取了一个图片,然后将它插入到一个 < img> 标签中。
这个最简单的用法中,fetch() 接受了一个参数——请求的地址——然后返回一个包含 response(一个 Response 对象)的 promise 对象。
当然它只是一个 HTTP 响应,而不是真的图片。为了获取图片的内容,我们需要使用 blob() 方法。
接着,从 Blob 中获取 objectURL,之后再插入到 img 中。

自定义请求参数

fetch()接收第二个可选参数,一个可以控制不同配置的init对象。

var myHeaders = new Hearders();vart myInit = {    method:'get',    headers:myHeaders,    mode:'cors',    cache:'default'};fetch('flowers,jpg',myInit).then(function(response){    return response.blob();}).then(function(myBlob) {  var objectURL = URL.createObjectURL(myBlob);  myImage.src = objectURL;});

检测请求是否成功

如果遇到网络故障,fetch() promise 将会 reject,带上一个 TypeError 对象。
想要精确的判断 fetch() 是否成功,需要包含 promise resolved 的情况,此时再判断 Response.ok 是不是为 true。

fetch('flowers.jpg').then(function(response) {  if(response.ok) {    response.blob().then(function(myBlob) {      var objectURL = URL.createObjectURL(myBlob);      myImage.src = objectURL;    });  } else {    console.log('Network response was not ok.');  }}).catch(function(error) {  console.log('There has been a problem with your fetch operation: ' + error.message);});

自定义请求对象

除了传给 fetch() 一个资源的地址,你还可以通过使用 Request() 构造函数来创建一个 request 对象,然后再作为参数传给 fetch()

var myHeaders = new Headers();var myInit = { method: 'GET',               headers: myHeaders,               mode: 'cors',               cache: 'default' };var myRequest = new Request('flowers.jpg',myInit);fetch(myRequest,myInit).then(function(response) {  return response.blob();}).then(function(myBlob) {  var objectURL = URL.createObjectURL(myBlob);  myImage.src = objectURL;});

Request() 和 fetch() 接受同样的参数。你甚至可以传入一个已存在的 request 对象来创造一个拷贝:

var anotherRequest = new Request(myRequest,myInit);

这个很有用,因为 request 和 response bodies 只能被使用一次(因为设计成了 stream 的方式,所以它们只能被读取一次)。创建一个拷贝就可以再次使用 request/response 了。

实例

在我们自己的项目里,现在都是应用ajax的:

function ajaxNode(page){        $.get("nodeAdmin/ajaxNode",            {                page:page-1,        },function(data){            $("#nodeList").html(data);        })     }

试着更换成fetch:

function ajaxNode(page){        fetch("nodeAdmin/ajaxNode",{page:page-1}).then(function(response){            return response.text();        }).then(function(data){            $("#nodeList").html(data);        })    }

it works!而且确实发现Promise这种链式操作非常清晰。

缺点

  • 使用 fetch 无法取消一个请求。这是因为 Fetch API 基于 Promise,而 Promise 无法做到这一点。

由于 Fetch 是典型的异步场景,所以大部分遇到的问题不是 Fetch 的,其实是 Promise 的。

最新文章

123

最新摄影

微信扫一扫

第七城市微信公众平台