學網頁設計很久了,但是之前一直都沒搞明白JavaScrip的異步處理,以及對應的fetch、promise、then、catch的使用。
前一陣子終於悟了,所以寫出來也幫助一下和之前的我一樣的人。
緒論
假如我們想做一個閱讀器,
運行時它會在div#document_content中另外一個Html的內容。
同時,它旁邊會有一個小小的div作爲目錄,會顯示這個被顯示在document_content的頁面的目錄(也就是提取出所有的h1、h2等顯示出來,並且可以點擊來滾動跳轉
-
使用fetch即可通過請求獲取其它網頁的內容。
fetch是一共用來進行一個HTTP請求。它會返回一個Promise對象
Promise,可以理解爲一個承諾,某個操作可能沒法立刻有結果,但是承諾在完成後會給一個結果。由於通常用來處理一些網絡連接之類的可能會失敗,還會允許承諾出結果時附帶一個狀態:
Resolved表示操作成功,Rejected表示操作失敗。加上還有一個承諾未出結果時的狀態,一共三個狀態。
-
就比如說,讓舍友去帶炒麪,舍友答應了,也就是給了一個承諾幫你帶飯,但是去買需要時間並不能立刻買到,他給你的只是一個承諾,不是買的結果。你需要等待這個承諾的結果。
但是去到飯堂,可能炒麪已經賣完了。
那沒賣完舍友就給你買了一份帶回來;賣完了舍友可能就回來告訴你賣完了,或者在微信告訴你賣完了。
然後作爲call了“請幫我帶炒麪”這個函數的人,如果承諾的結果是買到了,那你就喫就行;要是結果是沒買到,你就得看看是怎麼樣是喫別的還是不喫了。也就是對承諾的結果進行處理。
而具體怎麼判斷承諾的結果、處理,則是用then。
-
我們常用的fetch等會產生Promise的方法,我們就只管使用就好;而如果是自己定義的長耗時的函數,自己創造一個promise對象,就要包含怎麼樣是成功,怎麼樣是失敗了。
then是Promise對象的一個方法,用來設置如果Promise的結果是執行成功做什麼,設置一個回調函數。
通常來講,我們會這樣寫:
fetch(鏈接)
.then(respond => {
// 成功時怎麼處理
})
.catch(error =>{
// 失敗時怎麼處理
})
實際上就是
fetch(鏈接).then().catch()
的意思,就是把。因爲不需要把這個promise賦值給某個變量存起來,也就直接這樣就好了。
而catch則是Promise對象設置如果是執行失敗會做什麼。
例子
function load_doc(url_doc){
fetch(url_doc)
.then(response=> response.text())
.then(html => {
document.getElementById("document_content").innerHTML = html;
})
}
像這樣,就能完成我們剛剛說的把另外一個網頁的內容加載到這裏面去(但是注意不能跨域訪問)。
接下來是詳細的介紹。
-
fetch
fetch(url, options)
url爲請求的URL。
options(可選)用來指定請求的方式、請求頭等。默認是GET。
會返回一個Promise,若執行成功會隱式傳遞一個response給fetch.then()傳入的回調函數
注意,fetch並不是只是用來獲取數據,也可以用於向服務器post前端的數據。這種情況下,其實我們並不關心fetch的response,可以直接不then,只.catch即可。
fetch(url)
.then(response => { })
.catch(error => { })
response是一個對象,包含了status、headers,有.text()、.json()
注意,通常我們都要獲得response的內容,也就是調用response.text()或response.json()。注意,這兩個也是會返回一個promise,因爲處理可能也要耗時較久
它的結果會隱式傳遞
也就是這樣寫,把.text()給解析了:
let testing_doc = "../docs/common/tf_card/tf_card.html";
fetch(testing_doc)
.then(response => {
response.text()
.then(html => {
console.log(html)
})
})
但是這樣寫會嵌套調用.then,並不好。我們可以
let testing_doc = "../docs/common/tf_card/tf_card.html";
fetch(testing_doc)
.then(response => {
return response.text()
})
.then(html => {
console.log(html)
})
注意,這樣其實就是
fetch().then().then()
因爲then()的返回值就是傳進去的那個函數的返回值,所以他就能傳到下一個
千萬別忘了return,否則fetch().then()就是未定義,那再來.then()一下還是未定義。
還有一種簡便寫法:
let testing_doc = "../docs/common/tf_card/tf_card.html";
fetch(testing_doc) .then(
response => response.text())
.then(html => {
console.log(html)
})
利用了箭頭函數如果代碼一行,可以寫到一行省略{},則可以省去return自動返回該行的寫法。
-
Promise
看了前面講fetch的部分,你應該對promise的應用有了基本的認識。
接下來講點更底層的東西。
.then()和.catch()都是promise的方法。
那麼爲什麼是fetch().then().catch()呢?
是因爲,then()返回的也是一個promise!
然後,如果發生了錯誤,就會詳細傳遞。
比如說
fetch() .then() // then1
.then() // then2
.then() // then3
.catch()
.then() // then4
如果是then2發生了錯誤,後續的then將不執行(也就是then3不執行),一路向下傳遞,直到catch那裏。然後,catch之後,被視爲錯誤已經被捕獲了,它就不會一直累加了,catch後續的then仍然可以正常運行。也就是說,then4還是能執行的。
-
前面講到鏈式解析和嵌套解析,說鏈式解析更好。
這是因爲,鏈式解析的可讀性更強。其次,像上面演示的這個這樣鏈式解析,錯誤會一直向下傳遞,一個catch就可以捕獲前面的所有then的錯誤。如果是寫成嵌套解析,那麼就無法這樣。
async 和 await
如果不想用then,可以用async和await,讓代碼看起來像同步的。
async function loadData() {
let response = await fetch('data.json'); // 等待 fetch 完成
let data = await response.json(); // 等待 json() 解析
console.log("數據:", data);
}
loadData();
更多遊戲資訊請關註:電玩幫遊戲資訊專區
電玩幫圖文攻略 www.vgover.com