Fetch() API 到達視窗物件中,想要取代 XHR
那麼長的 XMLHttpRequest
fetch()
可讓您發出類似 XMLHttpRequest (XHR) 的網路要求。主要的差別在於 Fetch API 使用 Promise,這不僅可啟用更簡潔、更簡潔的 API,不但能避免回呼堆積,也不必記住 XMLHttpRequest 的複雜 API。
自 Chrome 40 版以來,Service Worker 的全域範圍就已提供 Fetch API,但會在 Chrome 42 的視窗範圍內啟用。另外,您也可以立即使用 GitHub 提供的 polyfill。
如果您從未使用過 Promise,請參閱 JavaScript Promise 簡介。
基本擷取要求
首先,比較使用 XMLHttpRequest
和 fetch
實作的簡易範例。我們只想要求網址、取得回應,並將網址剖析為 JSON。
XMLHttpRequest
XMLHttpRequest
需要設定兩個事件監聽器來處理成功和錯誤情況,以及呼叫 open()
和 send()
。MDN 文件範例:
function reqListener() {
var data = JSON.parse(this.responseText);
console.log(data);
}
function reqError(err) {
console.log('Fetch Error :-S', err);
}
var oReq = new XMLHttpRequest();
oReq.onload = reqListener;
oReq.onerror = reqError;
oReq.open('get', './api/some.json', true);
oReq.send();
擷取
我們的擷取要求看起來像這樣:
fetch('./api/some.json')
.then(
function(response) {
if (response.status !== 200) {
console.log('Looks like there was a problem. Status Code: ' +
response.status);
return;
}
// Examine the text in the response
response.json().then(function(data) {
console.log(data);
});
}
)
.catch(function(err) {
console.log('Fetch Error :-S', err);
});
首先,我們先檢查回應狀態是否為 200,然後再將回應剖析為 JSON。
fetch()
要求的回應是串流物件,這表示我們呼叫 json()
方法時,會傳回 Promise,因為串流的讀取作業會以非同步方式進行。
回應中繼資料
在先前的範例中,我們查看了 Response 物件的狀態,以及如何將回應剖析為 JSON。以下為我們可能想存取的其他中繼資料 例如標頭
fetch('users.json').then(function(response) {
console.log(response.headers.get('Content-Type'));
console.log(response.headers.get('Date'));
console.log(response.status);
console.log(response.statusText);
console.log(response.type);
console.log(response.url);
});
回應類型
當我們提出擷取要求時,回應會提供「basic
」、「cors
」或「opaque
」的 response.type
。這些 types
表示資源的來源,可用來指出應如何處理回應物件。
對相同來源上的資源提出要求時,回應會具有 basic
類型,而且回應上可查看的內容沒有限制。
如果在其他來源上針對資源發出要求,且傳回COR 標頭,則類型為 cors
。cors
和 basic
回應幾乎完全相同,差別在於 cors
回應會限制您可查看的標頭為 Cache-Control
、Content-Language
、Content-Type
、Expires
、Last-Modified
和 Pragma
。
opaque
回應適用於針對不同來源的資源,該資源不會傳回 CORS 標頭的要求。如果使用不透明的回應,我們將無法讀取傳回的資料或查看要求的狀態,因此無法檢查要求是否成功。
您可以定義擷取要求的模式,只解析特定要求。您可以設定的模式如下:
same-origin
只會針對相同來源上的資產要求成功,其他所有要求都會遭到拒絕。cors
會允許要求位於相同來源和其他來源上,且傳回適當 COR 標頭的資產。cors-with-forced-preflight
一律會在提出實際要求之前執行預檢。no-cors
的作用是向其他來源發出要求,但這些來源沒有 CORS 標頭並產生opaque
回應,但如前所述,目前在視窗全域範圍中無法辦到。
如要定義模式,請將選項物件新增為 fetch
要求的第二個參數,並在該物件中定義模式:
fetch('http://some-site.com/cors-enabled/some.json', {mode: 'cors'})
.then(function(response) {
return response.text();
})
.then(function(text) {
console.log('Request successful', text);
})
.catch(function(error) {
log('Request failed', error)
});
鏈結狀態
承諾的一大優勢是能夠將兩者鏈結在一起。以擷取來說,這可讓您在不同的擷取要求之間共用邏輯。
如果您使用的是 JSON API,您必須檢查狀態,並剖析每個回應的 JSON。您可以在傳回承諾的個別函式中定義狀態和 JSON 剖析,藉此簡化程式碼,這樣您就不必費心處理最終資料和錯誤情況。
function status(response) {
if (response.status >= 200 && response.status < 300) {
return Promise.resolve(response)
} else {
return Promise.reject(new Error(response.statusText))
}
}
function json(response) {
return response.json()
}
fetch('users.json')
.then(status)
.then(json)
.then(function(data) {
console.log('Request succeeded with JSON response', data);
}).catch(function(error) {
console.log('Request failed', error);
});
我們會定義用來檢查 response.status 的 status
函式,並傳回 Promise.resolve()
或 Promise.reject()
的結果,後者會傳回已解決或遭拒的 Promise。這是我們 fetch()
鏈結中呼叫的第一個方法,如果可以解析,我們就會呼叫 json()
方法,再次從 response.json()
呼叫傳回 Promise。之後我們會產生剖析的 JSON 物件。如果剖析失敗,Promise 會遭到拒絕,而擷取陳述式便會執行。
最棒的是,您可以在所有擷取要求之間共用邏輯,進而更輕鬆地維護、讀取及測試程式碼。
POST 要求
網頁應用程式通常想使用 POST 方法呼叫 API,並在要求主體中提供部分參數。
方法是在 fetch()
選項中設定 method
和 body
參數。
fetch(url, {
method: 'post',
headers: {
"Content-type": "application/x-www-form-urlencoded; charset=UTF-8"
},
body: 'foo=bar&lorem=ipsum'
})
.then(json)
.then(function (data) {
console.log('Request succeeded with JSON response', data);
})
.catch(function (error) {
console.log('Request failed', error);
});
使用擷取要求傳送憑證
如果您想使用 Cookie 等憑證提出擷取要求,應將要求的 credentials
設為 "include"
。
fetch(url, {
credentials: 'include'
})
常見問題
如何取消 fetch() 要求?
目前無法取消擷取作業,但需要在 GitHub 上探討,這個連結的 H/T @jaffathecake。
是否有 polyfill?
GitHub 有可擷取的 polyfill。H/T @Nexii。
為什麼服務工作處理程序支援「沒有線路」功能,但不支援窗口?
這與安全考量的關係,請按這裡瞭解詳情。