api 程式碼 (置頂)
fetch(url, {
method: 'POST',
body: JSON.stringify(data),
headers: new Headers({
'Content-Type': 'application/json'
})
}).then(res => res.json())
.catch(error => console.error('Error:', error))
.then(response => console.log('Success:', response));
初探 Fetch
當我們在 JavaScript 使用 fetch() 函式傳入一段 url,它會 return 一個 promise ,這個 promise 是一個獨特的物件。
const result = fetch('https:......')
console.log(result) // Promise {<pending>}
要拿到 Promise 裡面的資料,我們需要使用 .then() 這個函式,並傳入一個 callback function
fetch('https:......').then(response => {
console.log(response.status); // 200
})
成功從 Promise 拿到資料後,我們希望取得 response.body 裡的文字,我們需要使用 text() 這個函式
fetch('https:......').then(response => {
console.log(response.text()); // Promise
})
會發現 response.text() 會 return 一個 Promise 物件,我們已經知道要從 Promise 裡拿資料要用 .then(),於是可以把程式碼改成
fetch('https:......').then(response => {
response.text().then(text => {
console.log(text); // 成功拿到 response.body 的 json 資料
})
})
但是我們希望把 body 裡的 json 資料轉換回物件,可以用 json() 這個函式(他跟 JSON.parse() 的效果是一樣的),但是這個函式一樣會 return 一個 Promise 物件,所以我們要用 .then 傳入一個 callback function 來印出資料
fetch('https:......').then(response => {
response.json().then(text => {
console.log(text); // 拿到 response.body 轉成的物件
})
})
.then() 神奇的地方
如果在 .then() 裡面回傳一個值,這個值就會成為下一個 .then() 的參數
fetch().then(test => {
return 'Hello'; // 在 .then() 裡面回傳 'Hello' 出去
}).then(str => {
// 下一個 .then() 中的 str 就會是剛剛 return 的 'Hello'
console.log(str); // Hello
})
如果在 .then() 裡面回傳一個 Promise,它會幫你把這個 Promise 解析完之後,傳給下一個 .then()
fetch('https:......').then(response => {
return response.json() // 回傳一個 Promise
}.then(text => {
console.log(text); // 拿到 response.body 轉成的物件
})
.catch() 錯誤處理
對於 fetch() 來說,錯誤的意思是要連 response 都拿不到才會叫做錯誤,如果只是 400 ERROR 回傳了一個 response.status 這個不是錯誤,只要有拿到 response 都不是錯誤。
- 因為 fetch() 是非同步的,所以要做錯誤處理,需要在最後加一個 .catch() 去接住它的 error
fetch('https:......').then(response => {
return response.json();
}.then(text => {
console.log(text);
}).catch(err => {
console.log(err);
})
- 這種 .catch() 的方式,跟 try{ ... }.catch(e){ ... } 是完全不同的,這邊的方式是非同步的,try catch 處理的是同步的資料
POST method
參考網站:https://developer.mozilla.org/zh-TW/docs/Web/API/Fetch_API/Using_Fetch
- fetch() 的第一個參數是 url,第二個參數放一個物件,寫法如下:
fetch(url, {
method: 'POST',
body: JSON.stringify(data), // 資料可以是字串或物件
headers: new Headers({ // 要設定 header 要 new 一個物件
'Content-Type': 'application/json'
})
}).then(res => res.json())
.catch(error => console.error('Error:', error))
.then(response => console.log('Success:', response));
fetch 的使用注意事項
- Content-Type 裡面放的東西,需要對應到 body 的內容,要先去查看 server 接收的是什麼樣的資料,才來決定你的 Content-Type 是什麼
- 如果是 cross origin 跨網域發送的 api,fetch 方法在預設條件下是不會帶 cookie 上去的,所以如果要使用 fetch 在跨網域的情況下發送帶有 cookie 的 api,需要加上一行 credentials: 'include' 指令來設定:
fetch(url, {
method: 'POST',
body: JSON.stringify(data),
headers: new Headers({
'Content-Type': 'application/json'
}),
credentials: 'include' // 加上這行指令
}).then(res => res.json())
.catch(error => console.error('Error:', error))
.then(response => console.log('Success:', response));
- mode: 'no-cors' 這行指令的設定並不是說能夠突破 server cors 的限制,那是不可能的。這行指令只是在告訴瀏覽器:我明白 server 有做 cors 的設定,我本來就沒有要拿到 response,所以請你不必跳錯誤訊息給我。 (瀏覽器就不會跳出錯誤訊息,但是會給你一個空的 response 表示這段 api 的發送是有成功的)
自製一個 Promise
- 我們可以 new 一個 Promise 傳入一個 function,這個 function 要做為這個 Promise 的設定
function init(resolve, reject) { // resolve, reject 是兩個 function ,用來決定這個 Promise 裡的資料內容
resolve('Hello'); // 當需要回傳資料的時候,就 call resolve
reject('error~~~'); // 當需要觸發錯誤的時候,就 call reject
}
const myPromise = new Promise(init);
myPromise.then(data => {
console.log(data) // Hello
}).catch(err => {
console.log(err) // error~~~
})
簡化一點,會寫成下面這樣:
const myPromise = new Promise((resolve, reject) => {
resolve('Hello');
reject('error~~~');
});
XHR 包成 Promise 範例
const myPromise = new Promise((resolve, reject) => {
var request = new XMLHttpRequest();
request.open('GET', '/my/url', true);
request.onload = function() {
if (this.status >= 200 && this.status < 400) {
let data = JSON.parse(this.response);
resolve(data);
}
};
request.onerror = function(err) {
reject(err);
};
request.send();
});
myPromise.then(data => {
console.log('myPromise data', data)
}).catch(err => {
console.log('error', err)
})
- new Promise 裡面可以放任何同步或非同步的事情,再利用 resolve, reject 來設定在 call promise 會拿到的資料
實作一個可以自訂睡眠時間的 Promise
function sleep(ms) {
return new Promise(resolve => {
setTimeOut(resolve, ms);
})
}
sleep(1500).then(data => { // 等過了 1500ms 後才會執行 .then() 的動作
console.log(data);
})
上面那段程式碼,經過簡化後,可以變成一個非常簡短的樣子:
const sleep = ms => new Promise(resolve => setTimeOut(resolve, ms))
async 與 await
當我們想要用同步的寫法來寫 fetch,但是又要能達成非同步的功能,我們可以利用 async 與 await 來達成
// 非同步寫法:
function main() {
console.log('開始')
sleep(5000).then(() => {
console.log('結束')
})
}
// 同步寫法
async function main() {
console.log('開始')
await sleep(5000)
console.log('結束')
}
- async 代表著這個 function 是非同步的
- await 後面接一個 Promise,就會等到這個 Promise 執行完畢,才會繼續下一行,但是因為是非同步的,所以整個畫面不會為了等那幾秒鐘被 block 住
實際 api 串接
const getData = () => fetch(https:....).then(response => response.json)
async function main() {
let result
try { // 這個時候就可以使用 try catch 的寫法了
result = await getData()
} catch(err) {
console.log(err)
}
console.log(result)
}
main()
補充
製作 api 好用網站: Mocky