JavaScript 学习笔记 - 网络请求与远程资源(四)

from 《JavaScript 高级程序设计》第四版 第24章 网络请求与远程资源

--------------------------------------------------------------------------------------------------------------------

一、Fetch API

1. 基本用法

fetch('bar.txt').then((response) => {  // 请求完成、资源可用时,期约会解决为一个Response 对象

  console.log(response);  // Response { type: "basic", url: ...}

  response.text().then((data) => {  // text() 方法返回一个期约,会解决为取得资源的完整内容,纯文本格式的内容

    console.log(data);

  }

});

// 推荐使用以下写法,更简洁明了

fetch('bar.txt')

  .then((response) => response.text())

  .then((data) => console.log(data));

 

2. 处理状态码和请求失败

只要服务器返回了响应,fetch() 期约都会解决。至于真正的“成功”请求,则需要在处理响应时再定义。

因为服务器没有响应而导致浏览器超时,这样真正的fetch() 失败会导致期约被拒绝

fetch('/hangs-forever')

  .then((response) => {

    console.log(response.status);  // 200 or 404 or 500...

    console.log(response.statusText);  // OK or Not Found or Internal Server Error

    console.log(response.ok);  // true or false

  }, (err) => {

    console.log(err);

  });

// (浏览器超时后)

// TypeError: "NetworkError when attempting to fetch resource."

 

3. 自定义选项

只使用URL 时,fetch() 会发送 GET 请求,只包含最低限度的请求头。要进一步配置如何发送请求,需要传入可选的第二个参数 init 对象。使用 init 对象参数,可以配置 fetch() 在请求体中发送各种序列化的数据。

 

4. 常见 Fetch 请求模式

1)发送 JSON 数据

let payload = JSON.stringify({

  foo: 'bar'

});

let jsonHeaders = new Headers({

  'Content-Type': 'application/json'

});

fetch('/send-me-json', {

  method: 'POST',  // 发送请求体时必须使用一种HTTP方法

  body: payload,

  headers: jsonHeaders

});

2)在请求体中发送参数

// 因为请求体支持任意字符串值,所以可以通过它发送请求参数

let payload = 'foo=bar&baz=qux';

let paramHeaders = new Headers({

  'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'

});

fetch('/send-me-params', {

  method: 'POST',

  body: payload,

  headers: paramHeaders

});

3) 发送文件

因为请求体支持 FormData 实现,所以 fetch() 也可以序列化并发送文件字段中的文件:

let imageFormData = new FormData();

let imageInput = document.querySelector("input[type='file']");

imageFormData.append('image', imageInput.files[0]);

fetch('/img-upload', {

  method: 'POST',

  body: imageFormData

});

这个 fetch() 实现可以支持多个文件

let imageFormData = new FormData();

let imageInput = document.querySelector("input[type='file'][multiple]");

for (let i = 0; i < imageInput.files.length; ++i) {

  imageFormData.append('image', imageInput.files[i]);

}

fetch('/img-upload', {

  method: 'POST',

  body: imageFormData

});

4)加载 Blob文件

const imageElement = document.querySelector('img');

fetch('my-image.png')

  .then((response) => response.blob())

  .then((blob) => {

    imageElement.src = URL.createObjectURL(blob);

  });

5)发送跨源请求

6)中断请求

Fetch API 支持通过 AbortController/AbortSignal 对中断请求。调用 AbortController.abort() 会中断所有网络传输,特别适合希望停止传输大型负载的情况。中断进行中的 fetch() 请求会导致包含错误的拒绝。

let abortController = new AbortController();

fetch('wikipedia.zip', {signal: abortController.signal})

  .catch(() => console.log('aborted!');

// 10毫秒后中断请求

setTimeOut(() => abortController.abort(), 10);

// 已经中断

 

二、Headers 对象

Request.prototype.headers, Response.prototype.headers,另外,使用 new Headers() 也可以创建一个新实例。Headers对象与 Map对象极为相似。都有 get()、set()、has() 和 delete() 等实例方法。当然 Headers 并不是与 Map处处都一样,在初始化 Headers对象时,也可以使用键/值对形式的对象,而Map 则不可以;Headers 对象还可以通过 append() 方法添加多个值。

 

三、Request 对象

通过fetch 使用 Request会将请求体标记为已使用。也就是说,有请求体的 Request只能在一次 fetch中使用。要想基于包含请求体的相同 Request对象多次调用 fetch(),必须在第一次发送 fetch() 请求前调用 clone() :

let r = new Request('https://foo.com', { method: 'POST', body: 'foobar'});

// 3个都会成功

fetch(r.clone());

fetch(r.clone());

fetch(r);

 

四、Response 对象

有响应体的 Response对象只能读取一次。(不包含响应体的Response 对象不受此限制)比如:

let r = new Response('foobar');

r.text().then(console.log);  // foobar

r.text().then(console.log);

// TypeError: Failed to execute 'text' on 'Response': body stream is locked

要多次读取包含响应体的同一个 Response对象,必须在第一次读取前调用 clone():

let r = new Response('foobar');

r.clone().text().then(console.log);  // foobar

r.clone().text().then(console.log);  // foobar

r.text().then(console.log);  // foobar

 

五、Request、Response及 Body 混入

Request 和 Response 都使用了 Fetch API 的 Body混入,以实现两者承担有效载荷的能力。这个混入为两个类型提供了只读的body 属性(实现为 ReadableStream)、只读的 bodyUsed 布尔值(表示body 流是否已读)和一组方法,用于从流中读取内容并将结果转换为某种 JavaScript 对象类型。

通常,将 Request 和 Response 主体作为流来使用主要有两个原因。一个原因是有效载荷的大小可能会导致网络延迟,另一个原因是流 API 本身在处理有效载荷方面是有优势的。除此之外,最好是一次性获取资源主体。

Body 混入提供了5个方法,用于将 ReadableStream 转存到缓冲区的内存里,将缓冲区转换为某种 JavaScript 对象类型,以及通过期约来产生结果。在解决之前,期约会等待主体流报告完成及缓冲被解析。这意味着客户端必须等待响应的资源完全加载才能访问其内容。

1. Body.text()

fetch('https://foo.com')

  .then((response) => response.text())

  .then(console.log);

2. Body.json()

fetch('https://foo.com/foo.json')

  .then((response) => response.json())

  .then(console.log);  // {"foo": "bar"}

3. Body.formData()

fetch('https://foo.com/form-data')

  .then((response) => response.formData())

  .then((formData) => console.log(formData.get('foo'));

4. Body.arrayBuffer()

fetch('https://foo.com')

  .then((response) => response.arrayBuffer()).

  .then(console.log);  // ArrayBuffer(...) { }

5. Body.blob

fetch('https://foo.com')

  .then((response) => response.blob())

  .then(console.log);  // Blob(...) {size:..., type: "..."}

6. 一次性流

因为 Body 混入是构建在ReadableStream 之上的,所以主体流只能使用一次。这意味着所有主体混入方法都只能调用一次,再次调用就会抛出错误。

7. 使用 ReadableStream 主体

posted @ 2022-02-12 21:25  黄燃  阅读(283)  评论(0编辑  收藏  举报