第五节: 前后端交互之Promise用法和Fetch用法
一. Promise相关
1.说明
主要解决异步深层嵌套的问题,promise 提供了简洁的API 使得异步操作更加容易 。
2.入门使用
我们使用new来构建一个Promise Promise的构造函数接收一个参数,是函数,并且传入两个参数:resolve,reject,分别表示异步操作执行成功后的回调函数和异步操作执行失败后的回调函数。
var p = new Promise(function(resolve, reject) { // 这里用于实现异步任务 setTimeout(function() { var flag = false; if (flag) { // 正常情况 resolve('hello'); } else { // 异常情况 reject('出错了'); } }, 100); }); p.then(function(data) { console.log(data) }, function(info) { console.log(info) });
补充:回调地狱问题解决?
3. 基本Api
then-catch-finally, 其中then-catch等价于 then(fun1,fun2),fun2相当于catch。
代码:
1 function foo() { 2 return new Promise(function(resolve, reject) { 3 setTimeout(function() { 4 // resolve(123); 5 reject('error'); 6 }, 100); 7 }) 8 } 9 // foo() 10 // .then(function(data){ 11 // console.log(data) 12 // }) 13 // .catch(function(data){ 14 // console.log(data) 15 // }) 16 // .finally(function(){ 17 // console.log('finished') 18 // }); 19 20 // -------------------------- 21 // 两种写法是等效的 22 foo() 23 .then(function(data) { 24 console.log(data) 25 }, function(data) { 26 console.log(data) 27 }) 28 .finally(function() { 29 console.log('finished') 30 });
4.对象方法
(1). all:方法接受一个数组作参数,数组中的对象(p1、p2、p3)均为promise实例(如果不是一个promise,该项会被用 Promise.resolve 转换为一个promise)。它的状态由这三个promise实例决定。
(2). race:Promise.race 方法同样接受一个数组作参数。当p1, p2, p3中有一个实例的状态发生改变(变为 fulfilled或 rejected ),p的状态就跟着改变。并把第一个改变状态的promise的返回值传给p的回调函数。
代码分享:
二. Fetch用法
补充:
参考 API文档:https://developer.mozilla.org/zh-CN/docs/Web/API/Fetch_API/Using_Fetch
其它参考:https://www.cnblogs.com/wonyun/p/fetch_polyfill_timeout_jsonp_cookie_progress.html
用到的服务端接口方法:
1 [Route("api/[controller]/[action]")] 2 [ApiController] 3 public class FirstController : ControllerBase 4 { 5 6 /******************************************下面是测试Get请求的相关方法***************************************************/ 7 8 #region 下面是测试Get请求的相关方法 9 [HttpGet] 10 public string GetInfor1(string userName, string pwd) 11 { 12 return $"{userName}+{pwd}"; 13 } 14 15 [HttpGet] 16 public string GetInfor2([FromQuery]UserInfor model) 17 { 18 return $"{model.userName}+{model.pwd}"; 19 } 20 [HttpGet] 21 //加上[FromQuery]也报错 22 public string GetInfor3([FromQuery]dynamic model) 23 { 24 return $"{model.userName}+{model.pwd}"; 25 } 26 27 #endregion 28 29 } 30 [Route("api/[controller]/[action]")] 31 [ApiController] 32 public class ThirdController : Controller 33 { 34 [HttpGet] 35 public IActionResult GetInfor4(string userName, string pwd) 36 { 37 return Json(new 38 { 39 userName, 40 pwd 41 }); 42 } 43 }
1.简介
Fetch API是新的ajax解决方案 Fetch会返回Promise, fetch不是ajax的进一步封装,而是原生js,不需要引入任何库,没有使用XMLHttpRequest对象。
2.基本用法
(1).通用格式 fetch().then().then().catch() ,第一个then处理返回类型,在第二个then里才拿到返回值,里面的function可以简化写法。
(2).默认是get请求。
代码分享:
//默认是Get请求的 fetch("https://localhost:44387/api/First/GetInfor1?userName=ypf&pwd=123456").then(function(data) { return data.text(); }).then(function(data) { console.log(data); }); //简洁写法: fetch("https://localhost:44387/api/First/GetInfor1?userName=ypf&pwd=123456").then(data => data.text()).then( data => { console.log(data); });
(3).Post请求同样分两种,表单提交和json提交,需要在headers设置 Content-Type类型。
代码如下:
1 //表单提交 2 fetch('https://localhost:44387/api/First/Login3', { 3 method: 'post', 4 body: 'userName=ypf&pwd=123456', 5 headers: { 6 'Content-Type': 'application/x-www-form-urlencoded' 7 }, 8 }) 9 .then(function(data) { 10 return data.text(); 11 }).then(function(data) { 12 console.log(data) 13 }); 14 //JSON提交 15 fetch('https://localhost:44387/api/First/Login2', { 16 method: 'post', 17 body: JSON.stringify({ 18 userName: "admin", 19 pwd: "123456" 20 }), 21 headers: { 22 'Content-Type': 'application/json', 23 'token': 'dsfsdf', 24 } 25 }) 26 .then(function(data) { 27 return data.text(); 28 }).then(function(data) { 29 console.log(data) 30 });
(4).如何处理请求错误?
根据上面的通用格式,肯定是在catch中处理,但是fetch返回的promise对于404、415、500这些错误是获取不到,进入不到catch,catch仅能获取由于网络延迟错误,所以这里需要再次封装一下,获取这些状态,进行throw抛出,让其进入catch即可。 案例如下:
1 //处理非网络错误导致的 错误 2 fetch("https://localhost:44387/api/First/GetInfor3?userName=ypf&pwd=123456").then(function(response) { 3 console.log(response); 4 if (response.status >= 200 && response.status < 300) { 5 return response; 6 } 7 const error = new Error(response.statusText); 8 error.response = response; 9 throw error; //检测到报错会进入到catch中 10 }).then(function(data) { 11 return data.text(); 12 }).then(function(data) { 13 console.log(data); 14 }).catch(function(error) { 15 console.log(error.response.status + '--' + error.response.statusText); 16 console.log('There has been a problem with your fetch operation: ', error.message); 17 });
PS: put和delete用法和post类似,这里不再演示。
3.返回数据的处理
(1).text():返回的数据是字符串,如果服务器端返回的是json,而这里用text接收,再下一个then里使用的时候需要转换一下,JSON.parse(data)。
(2).json():返回的数据直接转换成JOSN,后面直接使用即可。
(3).其它:arrayBuffer()、blob()、formData()。
代码分享:
1 //返回的数据是JSON格式的, 如果用data.text()接收,需要JSON.parse(data);转换一下 2 fetch("https://localhost:44387/api/Third/GetInfor4?userName=ypf&pwd=123456").then(function(data) { 3 return data.text(); 4 }).then(function(data) { 5 var myData = JSON.parse(data); 6 console.log(myData.userName + '--' + myData.pwd); 7 }) 8 //返回的数据是JSON格式的, 如果用data.json()接收,直接使用即可 9 fetch("https://localhost:44387/api/Third/GetInfor4?userName=ypf&pwd=123456").then(function(data) { 10 return data.json(); 11 }).then(function(data) { 12 console.log(data.userName + '--' + data.pwd); 13 })
4.其它参数详细配置
(1).method: 请求使用的方法,如 GET、POST, 默认是Get。
(2).headers: 请求的头信息,比如可以在里面设置Token、设置content-type类型。
(3).credentials: 请求的 credentials,如 omit、same-origin 或者 include。为了在当前域名内自动发送 cookie , 必须提供这个选项, 从 Chrome 50 开始,这个属性也可以接受 FederatedCredential 实例或是一个 PasswordCredential 实例。
(4). mode: 请求的模式,如 cors、 no-cors 或者 same-origin(默认值)。
(5).body: 请求的 body 信息:可能是一个 Blob、BufferSource、FormData、URLSearchParams 或者 USVString 对象。注意 GET 或 HEAD 方法的请求不能包含 body 信息。
(6).cache: 请求的 cache 模式: default 、 no-store 、 reload 、 no-cache 、 force-cache 或者 only-if-cached 。
(7).redirect: 可用的 redirect 模式: follow (自动重定向), error (如果产生重定向将自动终止并且抛出一个错误), 或者 manual (手动处理重定向). 在Chrome中,Chrome 47之前的默认值是 follow,从 Chrome 47开始是 manual。
(8).referrer: 一个 USVString 可以是 no-referrer、client或一个 URL。默认是 client。
(9).referrerPolicy: 指定了HTTP头部referer字段的值。可能为以下值之一: no-referrer、 no-referrer-when-downgrade、 origin、 origin-when-cross-origin、 unsafe-url 。
(10).integrity: 包括请求的 subresource integrity 值 ( 例如: sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=)。
分析总结:
(1). fetch发送请求默认是不发送cookie的,不管是同域还是跨域;那么问题就来了,对于那些需要权限验证的请求就可能无法正常获取数据,这时可以配置其credentials项,其有3个值:
A.omit: 默认值,忽略cookie的发送
B.same-origin: 表示cookie只能同域发送,不能跨域发送
C.include: cookie既可以同域发送,也可以跨域发送
PS:fetch默认对服务端通过Set-Cookie头设置的cookie也会忽略,若想选择接受来自服务端的cookie信息,也必须要配置credentials选项;
(2). fetch不支持超时timeout处理
(3). fetch不支持JSONP。目前比较成熟的开源JSONP实现fetch-jsonp给我们提供了解决方案,想了解可以自行前往。(https://github.com/camsong/fetch-jsonp)
(4). 与XHR2一样,fetch也是支持跨域请求的,只不过其跨域请求做法与XHR2一样,需要客户端与服务端支持;另外,fetch还支持一种跨域,不需要服务器支持的形式,具体可以通过其mode的配置项来说明。
A.same-origin:该模式是不允许跨域的,它需要遵守同源策略,否则浏览器会返回一个error告知不能跨域;其对应的response type为basic。
B.cors: 该模式支持跨域请求,顾名思义它是以CORS的形式跨域;当然该模式也可以同域请求不需要后端额外的CORS支持;其对应的response type为cors。
C.no-cors: 该模式用于跨域请求但是服务器不带CORS响应头,也就是服务端不支持CORS;这也是fetch的特殊跨域请求方式;其对应的response type为opaque。(重点!!!)
特别注意:no-cors该模式允许浏览器发送本次跨域请求,但是不能访问响应返回的内容(能访问通接口,但是不能拿到返回值),这也是其response type为opaque透明的原因。
1 //特殊跨域不需要服务器端进行设置,能访问通,但是拿不到响应结果 2 fetch('https://localhost:44387/api/First/Login3', { 3 method: 'post', 4 body: 'userName=ypf&pwd=123456', 5 headers: { 6 'Content-Type': 'application/x-www-form-urlencoded' 7 }, 8 mode: 'no-cors' 9 }) 10 .then(function(data) { 11 return data.text(); 12 }).then(function(data) { 13 console.log(data) 14 });
!
- 作 者 : Yaopengfei(姚鹏飞)
- 博客地址 : http://www.cnblogs.com/yaopengfei/
- 声 明1 : 如有错误,欢迎讨论,请勿谩骂^_^。
- 声 明2 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。