天天看點

第五節: 前後端互動之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)
   });      

補充:回調地獄問題解決? 

第五節: 前後端互動之Promise用法和Fetch用法

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的回調函數。

代碼分享:

第五節: 前後端互動之Promise用法和Fetch用法

二. 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

用到的服務端接口方法:

第五節: 前後端互動之Promise用法和Fetch用法
第五節: 前後端互動之Promise用法和Fetch用法
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     }      

View Code

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 : 原創部落格請在轉載時保留原文連結或在文章開頭加上本人部落格位址,否則保留追究法律責任的權利。