- fetch只能使用於javascript前端,不能使用於node.js後端
-
locals
是在response
裡面的子物件
Promise (此為JavaScript ES6的一個核心功能)
Promise
是在ES6裡面的新增功能。
Promise
是一個表示非同步運算的最終完成或失敗的物件。由於多數人使用預建立的 Promise,這個導覽會先講解回傳 Promise 的使用方式,之後再介紹如何建立。
基本上,一個 Promise 是一個根據附加給他的 Callback 回傳的物件,以取代傳遞 Callback 到這個函數。
舉例來說,下方的範例若用舊方式應該會有兩個 Callback,並根據成功或失敗來決定使用哪個:
function successCallback(result) {
console.log("It succeeded with " + result);
}
function failureCallback(error) {
console.log("It failed with " + error);
}
doSomething(successCallback, failureCallback);
而新作法會回傳一個 Promise,這樣你就可以附加 Callback:
let promise = doSomething();
promise.then(successCallback, failureCallback);
再簡單點:
doSomething().then(successCallback, failureCallback);
我們稱之為 非同步函數呼叫 。這個做法有許多好處。 ref: 使用 Promise - JavaScript | MDN
一個 Promise 有這些保證:
- Callback 不會在當次的迴圈運行結束前呼叫。
- Callback 用 .then 添加,在非同步運算結束後呼叫,像前面那樣。
- 複 Callback 可以透過重複呼叫 .then 達成。
但 Promise 主要的立即好處是串連。
Promise可以解決所謂callback hell的開發情況。callback hell如下圖所示:
寫法沒有問題,但是會對之後的維護造成困擾。
一個簡單的premise範例:
new Promise((resolve, reject) => {
console.log('Initial');
resolve();
})
.then(() => {
throw new Error('Something failed');
console.log('Do this');
})
.catch(() => {
console.log('Do that');
})
.then(() => {
console.log('Do this whatever happened before');
});
promise主要是做非同步(Async)的事情,若是要做同步(Sync)的事情就不需要使用promise。promise是我要做一件事情,但是這個事情不知道甚麼時候做完,我可能需要等待,我就會把處理的順序動作放在promise裡面。當promise成功完成,就呼叫resolve();
,然後進入到 .then(() => {})
,如果發生錯誤,就呼叫reject
,然後進入到.catch(() => {})
。
範例一
new Promise((resolve, reject) => {
setTimeout(function () {
resolve('Hello');
// reject('bad');
}, Math.random() * 1000);
})
.then(result => {
info.innerHTML += `${result} <br>`;
return 'hi';
})
.then(a => {
// throw new Error('abc');
info.innerHTML += `a: ${a} <br>`;
})
.catch(ex => {
info.innerHTML += `ex: ${ex.toString()} <br>`;
})
.then(b => {
info.innerHTML += `b: ${b} <br>`;
})
範例二
const p = new Promise((resolve, reject)=>{
setTimeout(function(){
reject('hello');
}, 300);
});
console.log(1);
p.then(result=>{
console.log('then:', result);
return 3;
})
.then(a=>{
console.log(`a: ${a}`);
})
.then(b=>{
console.log(`b: ${b}`);
})
.catch(error=>{
console.log(`error: ${error}`);
return 'from error';
})
.then(c=>{
console.log(`c: ${c}`);
})
console.log(2);
範例三
const p = new Promise((resolve, reject)=>{
setTimeout(function(){
resolve('hello');
}, 300);
});
console.log(1);
p.then(result=>{
console.log('then:', result);
return new Promise((resolve, reject)=>{
setTimeout(function(){
resolve('hihi');
}, 300);
});
})
.then(a=>{
throw new Error('bad');
console.log(`a: ${a}`);
})
.catch(error=>{
console.log(`error: ${error}`);
})
console.log(2);
範例四
把要做的事情包成一個function,做完之後的結果return
一個promise
。
const myFunc = (msec)=>{
return new Promise((resolve, reject)=>{
setTimeout(function(){
resolve(Math.random()*100);
}, msec);
});
};
const data = [];
myFunc(1300)
.then(r=>{
data.push(r);
info.innerHTML += `${r} <br>`;
return myFunc(1400);
})
.then(r=>{
data.push(r);
info.innerHTML += `${r} <br>`;
return myFunc(1200);
})
.then(r=>{
data.push(r);
info.innerHTML += `${r} <br>`;
info.innerHTML += JSON.stringify(data);
})
若需要明確界定做作之間的順序相依性,就要使用promise。若不在乎何時做完,也不在乎某function之前要先完成哪些function,就不需要使用promise,因為就讓functions各自去執行即可。
通常使用promise
時,會搭配使用箭頭函式
,因為promise
跟箭頭函式
都同樣是ES6
的新功能,可以使用promise
就代表可以使用箭頭函式
了。
範例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="info">
<p>123</p>
</div>
<script>
function func(){
return new Promise((resolve, reject)=>{
setTimeout(function(){
resolve(Math.random())
}, 500)
})
}
func()
.then(r=>{
info.innerHTML += `<p>${r}</p>`;
})
</script>
</body>
</html>
另外也可以再使用 return func()
,再呼叫promise一次,範例如下:
function func(){
return new Promise((resolve, reject)=>{
setTimeout(function(){
resolve(Math.random())
}, 500)
})
}
func()
.then(r=>{
info.innerHTML += `<p>${r}</p>`;
return func();
})
.then(r=>{
info.innerHTML += `<p>${r}</p>`;
})
回到之前講的 fetch
的範例,程式如下:
const data={
email: $('#email').val(),
password: password.value
}
fetch('/try-json-post', {
method: 'POST',
headers: {
'content-type':'application/json'
},
body: JSON.stringify(data)
})
.then(r=>r.text())
.then(txt=>{
info.innerHTML=txt;
})
其中,r=>r.text()
是單行的箭頭函式,表示 return r.text()
。
不可以寫成下面的樣子:
r=>{
r.text()
}
這樣沒有return的效果。
如果不是單行的箭頭函式,return
要自己寫。如下:
r=>{
return r.text()
}
Fetch API 的 Response
介面代表了一個請求會返回的回應。
你可以用 Response.Response()
建構子創建一個新的 Response
物件。但實務上碰到 Response 物件的時機,比較常出現在進行了一個 API 操作後,得到返回的 Response 物件。舉例而言,使用 service worker Fetchevent.respondWith
或是使用單純的GlobalFetch.fetch()
。
上一個例子中,.then(r=>r.text())
,r=>
的 r
即為Fetch API的response的物件。
介面是一種類型,類別也是一種類型。
類別具有實作功能;沒有實作功能,只用來定義方法叫做介面。(參考JavaScript的物件導向,JavaScript的物件導向屬於殘缺版,並不完整。)
Takes a Response
stream and reads it to completion. It returns a promise that resolves with the result of parsing the body text as JSON
.
Takes a Response
stream and reads it to completion. It returns a promise that resolves with a USVString
(text).
Body.json()
與Body.text()
都是呼叫promise。