Fetch的使用

原文鏈接:https://www.cnblogs.com/libin-1/p/6853677.html

無論用JavaScript發送或獲取信息,我們都會用到Ajax。Ajax不需要刷新頁面就能發送和獲取信息,能使網頁實現異步更新。

幾年前,初始化Ajax一般使用jQuery的ajax方法:

$.ajax('some-url', {

????success:(data) => {/* do something with the data */ },

????error:(err) => {/* do something when an error happens */}

});

也可以不用jQuery,但不得不使用XMLHttpRequest,然而這是相當復雜

幸虧,瀏覽器現在支持Fetch API,可以無須其他庫就能實現Ajax

瀏覽器支持

桌面瀏覽器



手機/平板電腦



所有主要的瀏覽器(除了Opera Mini和老的IE)都支持Fetch。針對不支持的,可以使用Fetch polyfill

Fetch獲取數據

使用Fetch獲取數據很容易。只需要Fetch你想獲取資源。

假設我們想通過GitHub獲取一個倉庫,我們可以像下面這樣使用:

fetch('https://api.github.com/users/chriscoyier/repos');

Fetch會返回Promise,所以在獲取資源后,可以使用.then方法做你想做的。

fetch('https://api.github.com/users/chriscoyier/repos')?

????.then(response => {/* do something */})

如果這是你第一次遇見Fetch,也許驚訝于Fetch返回的response。如果console.log返回的response,會得到下列信息:

{

????body: ReadableStream?

????bodyUsed:false? headers: Headers?

????ok :true?

????redirected :false?

????status :200?

????statusText :"OK"?

????type :"cors"?

????url :"http://some-website.com/some-url"

? __proto__ : Response

}

可以看出Fetch返回的響應能告知請求的狀態。從上面例子看出請求是成功的(ok是true,status是200),但是我們想獲取的倉庫名卻不在這里。

顯然,我們從GitHub請求的資源都存儲在body中,作為一種可讀的流。所以需要調用一個恰當方法將可讀流轉換為我們可以使用的數據。

Github返回的響應是JSON格式的,所以調用response.json方法來轉換數據。

還有其他方法來處理不同類型的響應。如果請求一個XML格式文件,則調用response.text。如果請求圖片,使用response.blob方法。

所有這些方法(response.json等等)返回另一個Promise,所以可以調用.then方法處理我們轉換后的數據。

fetch('https://api.github.com/users/chriscoyier/repos')?

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

????.then(data => {

????????// data就是我們請求的reposconsole.log(data)

? });

可以看出Fetch獲取數據方法簡短并且簡單。

接下來,讓我們看看如何使用Fetch發送數據。

Fetch發送數據

使用Fetch發送也很簡單,只需要配置三個參數。

fetch('some-url', options);

第一個參數是設置請求方法(如post、put或del),Fetch會自動設置方法為get。

第二個參數是設置頭部。因為一般使用JSON數據格式,所以設置ContentType為application/json。

第三個參數是設置包含JSON內容的主體。因為JSON內容是必須的,所以當設置主體時會調用JSON.stringify。

實踐中,post請求會像下面這樣:

let content = {some:'content'};

// The actual

fetch requestfetch('some-url', {

????method:'post',

????headers: {

????????'Content-Type':'application/json'?

????},

????body:JSON.stringify(content)

})

// .then()...

Fetch處理異常

雖然希望Ajax響應成功,但是仍會有問題出現:

可能嘗試獲取不存在的資源

沒有權限獲取資源

輸入參數有誤

服務器拋出異常

服務器超時

服務器崩潰

API更改

...

假設我們試圖獲取不存在錯誤,并了解如何處理錯誤。下面的例子我將chriscoyier拼錯為chrissycoyier

// 獲取chrissycoyier's repos 而不是 chriscoyier's reposfetch('https://api.github.com/users/chrissycoyier/repos')

為了處理此錯誤,我們需要使用catch方法。

也許我們會用下面這種方法:

fetch('https://api.github.com/users/chrissycoyier/repos')?

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

????.then(data =>console.log('data is', data))?

.catch(error =>console.log('error is', error));

然而卻得到下面這樣結果:


獲取失敗,但是第二個.then方法會執行。

如果console.log此次響應,會看出不同:

{

????body: ReadableStream?

????bodyUsed:true?

????headers: Headers?

????ok:false????// Response is not ok?

????redirected:false?

????status:404// HTTP status is 404.

? ? statusText:"Not Found"http:// Request not found?

????type:"cors"?

????url:"https://api.github.com/users/chrissycoyier/repos"

}

大部分是一樣的,只有ok、status和statusText是不同的,正如所料,GitHub上沒有發現chrissycoyier。

上面響應告訴我們Fetch不會關心AJAX是否成功,他只關心從服務器發送請求和接收響應,如果響應失敗我們需要拋出異常。

因此,初始的then方法需要被重寫,以至于如果響應成功會調用response.json。最簡單方法是檢查response是否為ok。

fetch('some-url')?

????.then(response => {

????????if (response.ok) {

????????????return response.json()? ?

????????}else {

????????????// Find some way to get to execute .catch()

? ? ? ?}

? });

一旦我們知道請求是不成功的,我可以throw異常或rejectPromise來調用catch。

// throwing an Error

else {

????thrownewError('something went wrong!')

}

// rejecting a Promise

else {

????returnPromise.reject('something went wrong!')

}

這里選擇Promise.reject,是因為容易擴展。拋出異常方法也不錯,但是無法擴展,唯一益處在于便于棧跟蹤。

所以,到現在代碼應該是這樣的:

fetch('https://api.github.com/users/chrissycoyier/repos')?

????.then(response => {

????????if (response.ok) {

????????????return response.json()? ?

????????}else {

????????????returnPromise.reject('something went wrong!')? ?

????????}?

})?

.then(data =>console.log('data is', data))?

.catch(error =>console.log('error is', error));


這樣錯誤就會進入catch語句中。


但是rejectPromise時,只輸出字符串不太好。這樣不清楚哪里出錯了,你肯定也不會想在異常時,輸出下面這樣:


讓我們在看看響應:

在這個例子中,我們知道資源是不存在。所以我們可以返回404狀態或Not Found原因短語,然而我們就知道如何處理。

為了在.catch中獲取status或statusText,我們可以reject一個JavaScript對象:

上面的錯誤處理方法對于下面這些不需要解釋的HTTP狀態很適用。

401: Unauthorized

404: Not found

408: Connection timeout

...

但對于下面這些特定的錯誤不適用:

400:Bad request

例如,如果請求錯誤缺少必要的參數,就會返回400.

光在catch中告訴狀態及原因短語并不足夠。我們需要知道缺少什么參數。

所以服務器需要返回一個對象,告訴造成錯誤請求原因。如果使用Node和Express,會返回像下面這樣的響應:

res.status(400).send({

????err:'no first name'

})

無法在最初的.then方法中reject,因為錯誤對象需要response.json來解析。

解決的方法是需要兩個then方法。這樣可以首先通過response.json讀取,然后決定怎么處理。


首先我們調用response.json讀取服務器發來的JSON數據,response.json返回Promise,所以可以鏈式調用.then方法。

在第一個.then中調用第二個.then,因為我們仍希望通過repsonse.ok判斷響應是否成功。

如果想發送狀態和原因短語,可以使用Object.assign()將二者結合為一個對象。

可以使用這樣新的handleResponse函數,讓數據能自動的進入.then和.catch中。


處理其他響應類型

到現在,我們只處理JSON格式的響應,而返回JSON格式數據大約占90%。

至于其他的10%呢?

假設上面的例子返回的是XML格式的響應,也許會收到下面異常:


這是因為XML格式不是JSON格式,我們無法使用response.json,事實上,我們需要response.text,所以我們需要通過判斷響應的頭部來決定內容格式:


當我遇見這種問題時,我嘗試使用ExpressJWT處理身份驗證,我不知道可以發生JSON響應數據,所以我將XML格式設為默認。

這是我們到現在完整代碼:



介紹zlFetch

zlFetch庫就是上例中handleResponse函數,所以可以不用生成此函數,不需要擔心響應來處理數據和錯誤。

典型的zlfetch像下面這樣:

使用之前,需要安裝zlFetch

npm install zl-fetch --save

接著,需要引入到你的代碼中,如果你需要polyfill,確保加入zlFetch之前引入它。


zlFetch還能無須轉換成JSON格式就能發送JSON數據。

下面兩個函數做了同樣事情,zlFetch加入Content-type然后將內容轉換為JSON格式。


zlFetch處理身份認證也很容易。

常用方法是在頭部加入Authorization,其值設為Bearer your-token-here。如果你需要增加token選項,zlFetch會幫你創建此域。

所以,下面兩種代碼是一樣的:


下面就是使用zlFetch來從GitHub上獲取repos:

總結

Fetch是很好的方法,能發送和接收數據。不需要在編寫XHR請求或依賴于jQuery。

盡管Fetch很好,但是其錯誤處理不是很直接。在處理之前,需要讓錯誤信息進入到catch方法中。

使用zlFetch庫,就不需要擔心錯誤處理了。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,565評論 6 539
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,115評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,577評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,514評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,234評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,621評論 1 326
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,641評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,822評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,380評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,128評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,319評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,879評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,548評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,970評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,229評論 1 291
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,048評論 3 397
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,285評論 2 376

推薦閱讀更多精彩內容

  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,812評論 18 139
  • 現在應該很少有人用原生的JS內置XMLHttpRequest對象寫異步調用了,仍然用的比較多的應該是Jquery的...
    琉璃橙子閱讀 1,889評論 0 2
  • fetch是js提供進行網絡請求的框架。 調用結構是這樣的。 fetch( url , options ).the...
    lzh_coder閱讀 493評論 0 0
  • fetch已經被大部分的瀏覽器兼容了,包括chrome,Firefox,safari,opera,edge,但是I...
    風吹過的空氣閱讀 909評論 0 0
  • 開掛人生 一個有趣的事情就是娜姐邀請到DISC李海峰老師來成都分享,李海峰老師順帶把簡書一哥六哥,還有BM創始人敏...
    深井冰就是大神閱讀 129評論 0 0