JavaScript教程:一次性搞懂promise、then

學網頁設計很久了,但是之前一直都沒搞明白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