赞
踩
目录
(1)、通过手动控制 promise 状态的实例来实现 fetch 的 timeout 功能
(2)、利用 Promise.race 方法代替实现 fetch 的 timeout 的功能
3、解决 fetch 不支持进度事件(Progress Event)
(1)、利用 response.body 模拟实现 fetch 的 progress 事件
(2)、使用 Promise+XHR 结合的方式实现类 fetch 的 progress 效果
fetch 是一种 HTTP 数据请求的方式,它不是 ajax 。 fetch 是 XMLHttpRequest(以下简称 XHR)的一种替代方案。
fetch 与 ajax 的区别:
- fetch('http://example.com/movies.json')
- .then(function(response) {
- return response.json();
- })
- .then(function(myJson) {
- console.log(myJson);
- });
fetch 方法接受两个参数:一个 URL 地址或一个 request 对象 和 (可选的)一个配置项对象。
除了传给 fetch() 一个 URL 地址,还可以通过使用 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).then(function(response) {
- return response.blob();
- }).then(function(myBlob) {
- var objectURL = URL.createObjectURL(myBlob);
- myImage.src = objectURL;
- });
(可选的)一个配置项对象。该配置项包括所有对请求的设置。
配置项可选的参数有:
举个栗子:
- // POST方法实现示例
- postData('http://example.com/answer', {answer: 42})
- .then(data => console.log(data))
- .catch(error => console.error(error))
-
- function postData(url, data) {
- // 默认值标记为 *
- return fetch(url, {
- body: JSON.stringify(data), // 必须与'Content-Type'标头匹配
- cache: 'no-cache', // 可选的值有*default, no-cache, reload, force-cache, only-if-cached。
- credentials: 'same-origin', // 可选的值有include, same-origin, *omit
- headers: {
- 'user-agent': 'Mozilla/4.0 MDN Example',
- 'content-type': 'application/json'
- },
- method: 'POST', // 可选的值有*GET, POST, PUT, DELETE等。
- mode: 'cors', // 可选的值有no-cors, cors, *same-origin
- redirect: 'follow', // 可选的值有manual, *follow, error
- referrer: 'no-referrer', // 可选的值有*client, no-referrer
- })
- .then(response => response.json()) // 解析JSON响应
- }
fetch 方法,总是返回一个包含响应结果的 Promise 对象。当该 Promise 对象为 resolve 状态时,在其回调函数中可获取 Response 对象。
Response 的可配置参数包括:
Response 提供的方法如下:
当 fetch 方法返回的 promise 对象的状态是 resolved 时,调用 then 方法,在 then 方法的回调函数中判断 response.ok 为 true 时,才表示 fetch() 请求是成功的。否则,fetch() 请求就是失败的。
- fetch('flowers.jpg').then(function(response) {
- if(response.ok) {
- return response.blob();
- }
- throw new Error('Network response was not ok.');
- }).then(function(myBlob) {
- var objectURL = URL.createObjectURL(myBlob);
- myImage.src = objectURL;
- }).catch(function(error) {
- console.log('There has been a problem with your fetch operation: ', error.message);
- });
- var url = 'https://example.com/profile';
- var data = {username: 'example'};
-
- fetch(url, {
- method: 'POST', // 或者 'PUT'
- body: JSON.stringify(data), // 数据可以是“string”或{object}!
- headers: new Headers({
- 'Content-Type': 'application/json'
- })
- }).then(res => res.json())
- .catch(error => console.error('Error:', error))
- .then(response => console.log('Success:', response));
可以通过 HTML <input type="file" /> 元素,FormData() 和 fetch() 上传文件。
- var formData = new FormData();
- var fileField = document.querySelector("input[type='file']");
-
- formData.append('username', 'abc123');
- formData.append('avatar', fileField.files[0]);
-
- fetch('https://example.com/profile/avatar', {
- method: 'PUT',
- body: formData
- })
- .then(response => response.json())
- .catch(error => console.error('Error:', error))
- .then(response => console.log('Success:', response));
可以通过HTML <input type="file" mutiple/> 元素,FormData() 和 fetch() 上传文件。
- var formData = new FormData();
- var photos = document.querySelector("input[type='file'][multiple]");
-
- formData.append('title', 'My Vegas Vacation');
- // formData 只接受文件、Blob 或字符串,不能直接传递数组,所以必须循环嵌入
- for (let i = 0; i < photos.files.length; i++) {
- formData.append('photo', photos.files[i]);
- }
-
- fetch('https://example.com/posts', {
- method: 'POST',
- body: formData
- })
- .then(response => response.json())
- .then(response => console.log('Success:', JSON.stringify(response)))
- .catch(error => console.error('Error:', error));
- export default async(url = '', data = {}, type = 'GET', method = 'fetch') => {
- type = type.toUpperCase();
- url = baseUrl + url;
-
- if (type == 'GET') {
- let dataStr = ''; //数据拼接字符串
- Object.keys(data).forEach(key => {
- dataStr += key + '=' + data[key] + '&';
- })
-
- if (dataStr !== '') {
- dataStr = dataStr.substr(0, dataStr.lastIndexOf('&'));
- url = url + '?' + dataStr;
- }
- }
-
- if (window.fetch && method == 'fetch') {
- let requestConfig = {
- credentials: 'include',//为了在当前域名内自动发送 cookie , 必须提供这个选项
- method: type,
- headers: {
- 'Accept': 'application/json',
- 'Content-Type': 'application/json'
- },
- mode: "cors",//请求的模式
- cache: "force-cache"
- }
-
- if (type == 'POST') {
- Object.defineProperty(requestConfig, 'body', {
- value: JSON.stringify(data)
- })
- }
-
- try {
- const response = await fetch(url, requestConfig);
- const responseJson = await response.json();
- return responseJson
- } catch (error) {
- throw new Error(error)
- }
- } else {
- return new Promise((resolve, reject) => {
- let requestObj;
- if (window.XMLHttpRequest) {
- requestObj = new XMLHttpRequest();
- } else {
- requestObj = new ActiveXObject;
- }
-
- let sendData = '';
- if (type == 'POST') {
- sendData = JSON.stringify(data);
- }
-
- requestObj.open(type, url, true);
- requestObj.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
- requestObj.send(sendData);
-
- requestObj.onreadystatechange = () => {
- if (requestObj.readyState == 4) {
- if (requestObj.status == 200) {
- let obj = requestObj.response
- if (typeof obj !== 'object') {
- obj = JSON.parse(obj);
- }
- resolve(obj)
- } else {
- reject(requestObj)
- }
- }
- }
- })
- }
- }
支持 fetch 的浏览器版本有:Chrome、Firefox、Safari 6.1+ 和 IE 10+。
虽然,不是所有的浏览器都支持 fetch 请求,但是我们可以用window.fetch polyfill来处理兼容问题。
fetch 的 timeout 的特点:
实现 fetch 的 timeout 功能,其思想就是新创建一个可以手动控制promise状态的实例,根据不同情况来对新promise实例进行resolve或者reject,从而达到实现timeout的功能。
- 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)
- }
- )
- })
- }
- 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])
- }
Progress Events定义了与客户端服务器通信有关的事件。这些事件最早其实只针对XHR操作,但目前也被其它API借鉴。有以下6个进度事件:
Ajax 的 XHR 是原生支持 progress 事件的,比如:
- var xhr = new XMLHttpRequest()
- xhr.open('POST', '/uploads')
- xhr.onload = function() {}
- xhr.onerror = function() {}
- function updateProgress (event) {
- if (event.lengthComputable) {
- var percent = Math.round((event.loaded / event.total) * 100)
- console.log(percent)
- }
- xhr.upload.onprogress =updateProgress; //上传的progress事件
- xhr.onprogress = updateProgress; //下载的progress事件
- }
- xhr.send();
但 fetch 就不支持该事件。不过,fetch 内部设计实现了 Request 和 Response 类。其中 Response 封装一些方法和属性,通过 Response 实例可以访问这些方法和属性,例如 response.json()、response.body 等等。
response.body是一个可读字节流对象,其实现了一个getRender()方法,其具体作用是:用于读取响应的原始字节流,该字节流是可以循环读取的,直至body内容传输完成。因此,利用到这点可以模拟出 fetch 的 progress。
- // fetch() returns a promise that resolves once headers have been received
- fetch(url).then(response => {
- // response.body is a readable stream.
- // Calling getReader() gives us exclusive access to the stream's content
- var reader = response.body.getReader();
- var bytesReceived = 0;
-
- // read() returns a promise that resolves when a value has been received
- reader.read().then(function processResult(result) {
- // Result objects contain two properties:
- // done - true if the stream has already given you all its data.
- // value - some data. Always undefined when done is true.
- if (result.done) {
- console.log("Fetch complete");
- return;
- }
-
- // result.value for fetch streams is a Uint8Array
- bytesReceived += result.value.length;
- console.log('Received', bytesReceived, 'bytes of data so far');
-
- // Read some more, and call this function again
- return reader.read().then(processResult);
- });
- });
- function fetchProgress(url, opts={}, onProgress){
- return new Promise(funciton(resolve, reject){
- var xhr = new XMLHttpRequest();
- xhr.open(opts.method || 'get', url);
- for(var key in opts.headers || {}){
- xhr.setRequestHeader(key, opts.headers[key]);
- }
-
- xhr.onload = e => resolve(e.target.responseText)
- xhr.onerror = reject;
- if (xhr.upload && onProgress){
- xhr.upload.onprogress = onProgress; //上传
- }
- if ('onprogerss' in xhr && onProgress){
- xhr.onprogress = onProgress; //下载
- }
- xhr.send(opts.body)
- })
- }
- fetchProgress('/upload').then(console.log)
JSONP 是外链一个javascript资源。
如何基于Promise来实现一个JSONP,并且使其看起来就像 fetch 支持 JSONP 一样?
可以使用 fetch-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)
- })
XHR 2 级 支持一种跨域:
fetch 支持两种跨域请求:
总的来说,fetch 的跨域请求是使用 CORS 方式,需要浏览器和服务端的支持。
Ajax:它的全称是:Asynchronous JavaScript And XML,翻译过来就是“异步的 Javascript 和 XML”。
Ajax 是一个技术统称,是一个概念模型,它囊括了很多技术,并不特指某一技术(比如XMLHttpRequest),它很重要的特性之一就是:局部刷新页面,无需重载整个页面。
Ajax 是一种思想,XMLHttpRequest 只是实现 Ajax 的一种方式。
比如:jQuery AJAX
- $.ajax({
- url: url,
- method: 'GET', // 请求方法,可选项包括 GET、POST、PUT、DELETE 等
- dataType: 'json',
- headers: {
- 'Content-Type': 'application/json', // 请求头部信息
- },
- data: {
- key1: value1,
- key2: value2,
- // 传递查询参数
- },
- success: function(data) {
- // 处理返回的数据
- },
- error: function(xhr, status, error) {
- // 处理请求错误
- }
- });
Fetch 是在 ES6 出现的,它使用了 ES6 提出的 promise 对象。它是 XMLHttpRequest 的替代品。
Fetch 是一个 API,它是真实存在的,它是基于 promise 的。
很多小伙伴会把它与 Ajax 作比较,其实这是不对的,我们通常所说的 Ajax 是指使用 XMLHttpRequest 实现的 Ajax,所以真正应该和 XMLHttpRequest 作比较。
Fetch 的特点:
- fetch(url, {
- method: 'GET', // 请求方法,可选项包括 GET、POST、PUT、DELETE 等
- headers: {
- 'Content-Type': 'application/json', // 请求头部信息
- },
- body: JSON.stringify(data), // 请求体内容,通常用于 POST 或 PUT 请求
- })
- .then(response => response.json())
- .then(data => {
- // 处理返回的数据
- })
- .catch(error => {
- // 处理请求错误
- });
Axios 是随着 Vue 的兴起而被广泛使用的,目前来说,绝大多数的 Vue 项目中的网络请求都是利用 Axios 发起的。当然它并不是一个思想,或者一个原生 API,它是一个封装库。
Axios 是一个基于 promise 封装的网络请求库,它是基于 XHR 进行二次封装。
Axios 的特点:
- import axios from 'axios';
-
- axios.get(url, {
- params: {
- key1: value1,
- key2: value2,
- // 传递查询参数
- },
- headers: {
- 'Content-Type': 'application/json', // 请求头部信息
- },
- })
- .then(response => {
- // 处理返回的数据
- })
- .catch(error => {
- // 处理请求错误
- });
Js中fetch方法:https://www.cnblogs.com/WindrunnerMax/p/13024711.html
传统 Ajax 已死,Fetch 永生:javascript - 传统 Ajax 已死,Fetch 永生 - 会影 - SegmentFault 思否
XHR or Fetch API ?:XHR or Fetch API ? - Jartto's blog
res.json() 与 res.send() 的区别?:Express中 res.json 和res.end 及res.send()_Andy____Li的博客-CSDN博客
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。