JavaScript異步與Promise基本用法(resolve與reject)


Promise解決的問題
相信每個前端都遇到過這樣一個問題,當一個異步任務的執行需要依賴另一個異步任務的結果時,我們一般會將兩個異步任務嵌套起來,這種情況發生一兩次還可以忍,但是發生很多次之后,就形成了所謂的回調地獄,代碼層層嵌套,環環相扣,很明顯,邏輯稍微復雜一些,這樣的程序就會變得難以維護。就比如我們昨天的那個回調地獄的例子:

function funA(callback) { console.log("A"); setTimeout(() = > { callback() }, 100) } function funB() { console.log("B") } function funC(callback) { console.log("C") setTimeout(() = > { callback() }, 1000) } function funD() { console.log("D") } function funE() { console.log("E") } function funF() { console.log("F") } funA(() = > { funB() funC(() = > { funD() }) funE() }) funF()

 

 

對於這種情況,程序員們想了很多解決方案(比如將代碼模塊化),但流程控制上,還是沒有避免大量的嵌套。但在ES6之后的標准里,Promise的標准化,一定程度上解決了JavaScript的流程操作問題。

什么是Promise
在《異步與性能》的第三章中有這么個場景來比喻 Promise:

  我走到快餐店的櫃台前,點了一個起士漢堡。並交了1.47美元的現金。通過點餐和付款,我為得到一個 值(起士漢堡)制造了一個請求。我發起了一個事務。

  但是通常來說,起士漢堡不會立即到我手中。收銀員交給一些東西代替我的起士漢堡:一個帶有點餐排隊號的收據。這個點餐號是一個“我欠你”的許諾(Promise),它保證我最終會得到我的起士漢堡。

  於是我就拿着我的收據和點餐號。我知道它代表我的 未來的起士漢堡,所以我無需再擔心它——除了挨餓!

  在我等待的時候,我可以做其他的事情,比如給我的朋友發微信說,“嘿,一塊兒吃午餐嗎?我要吃起士漢堡”。

  我已經在用我的 未來的起士漢堡 進行推理了,即便它還沒有到我手中。我的大腦可以這么做是因為它將點餐號作為起士漢堡的占位符號。這個占位符號實質上使這個值 與時間無關。它是一個 未來的值。

  最終,我聽到,“113號!”。於是我愉快地拿着收據走回櫃台前。我把收據遞給收銀員,拿回我的起士漢堡。 換句話說,一旦我的 未來的值 准備好,我就用我的許諾值換回值本身。

  但還有另外一種可能的輸出。它們叫我的號,但當我去取起士漢堡時,收銀員遺憾地告訴我,“對不起,看起來我們的起士漢堡賣光了。”把這種場景下顧客有多沮喪放在一邊,我們可以看到 未來的值 的一個重要性質:它們既可以表示成功也可以表示失敗。

  每次我點起士漢堡時,我都知道我要么最終得到一個起士漢堡,要么得到起士漢堡賣光的壞消息,並且不得不考慮中午吃點兒別的東西。

  我由等待漢堡變成了等到或者等不到,這個過程不可逆,

上面很形象的介紹了promise,上面的等待漢堡和得到漢堡,漢堡賣光了,得不到漢堡,分別對應promise的三種狀態 **pending: 進行中,既不是成功,也不是失敗狀態。 fulfilled: 意味着操作成功完成。 rejected: 意味着操作失敗。**
Promise的基本用法
語法

new Promise( function(resolve, reject) {...} ); //reject參數 可不選

 

參數
executor

executor是帶有 resolve 和 reject 兩個參數的函數 。Promise構造函數執行時立即調用executor 函數, resolve 和 reject 兩個函數作為參數傳遞給executor(executor 函數在Promise構造函數返回新建對象前被調用)。resolve 和 reject 函數被調用時,分別將promise的狀態改為fulfilled(完成)或rejected(失敗)。executor 內部通常會執行一些異步操作,一旦完成,可以調用resolve函數來將promise狀態改成fulfilled,或者在發生錯誤時將它的狀態改為rejected。
如果在executor函數中拋出一個錯誤,那么該promise 狀態為rejected。executor函數的返回值被忽略。

對更多對Promise的描述感興趣的可以 點擊查看MDN Promise下面我們開始上代碼

新建一個Promise的實例:

let promise = new Promise((resolve, reject) = > { setTimeout(() = > { let random = Math.random() if (random > 0.5) { resolve(`resolve$ {random}`) } else { resolve(`reject$ {random}`) } }, 1000) })

 

由上所示,Promise的構造函數接收一個函數作為參數,該函數接受兩個額外的函數,resolve和reject,這兩個函數分別代表將當前Promise置為fulfilled(已成功)和rejected(已失敗)兩個狀態。Promise正是通過這兩個狀態來控制異步操作的結果。接下來我們將討論Promise的用法,實際上Promise上的實例promise是一個對象,不是一個函數。在聲明的時候,Promise傳遞的參數函數會立即執行,因此Promise使用的正確姿勢是在其外層再包裹一層函數。

 

let run = function() { return new Promise((resolve, reject) => { setTimeout(() => { let random = Math.random() if (random > 0.5) { resolve(`resolve:${random}`) } else { reject(`reject:${random}`) } }, 1000) }) } run()

 

 

這是Promise的正常用法,接下來,就是對異步操作結果的處理,接着上面創建的函數run()

run().then( function(value) { console.log(value) })

 


每個Promise的實例對象,都有一個then的方法,這個方法就是用來處理之前各種異步邏輯的結果。

then方法可以接受兩個回調函數作為參數。第一個回調函數是Promise對象的狀態變為resolved時調用,第二個回調函數是Promise對象的狀態變為rejected時調用。其中,第二個函數是可選的,不一定要提供。這兩個函數都接受Promise對象傳出的值作為參數。
下面是一個用Promise對象實現的 Ajax 操作的例子:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JavaScript異步</title>
</head>
<body>
<script src="https://unpkg.com/jquery@3.3.1/dist/jquery.min.js"></script>
<script>
new Promise((resolve, reject) => { $.ajax({ url: "https://easy-mock.com/mock/5c249dbe46e8386d0b21b475/example_copy_copy/promisetest", success: res => { if (res.code == 0) { resolve(res.data) } else { reject(res.desc) } } }); }) .then(res => { console.log(res); },err =>{ console.log(err) }) </script>

</body>
</html>

 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>JavaScript異步</title>
</head>
<body>
<script src="https://unpkg.com/jquery@3.3.1/dist/jquery.min.js"></script>
<script>
    var a = new Promise(
        //下面函數為executor函數,帶有 resolve 和 reject 兩個參數的函數 。Promise構造函數執行時立即調用executor 函數, resolve 和 reject 兩個函數作為參數傳遞給executor(executor 函數在Promise構造函數返回新建對象前被調用)。
        (resolve, reject) => {
        $.ajax({
            url: "https://api.apiopen.top/musicBroadcasting",
            success: res => {
                if (res.code == 200) {
                    console.log('1',res.result)
                    // resolve()異步回調將promise變為fulfilled(已成功)狀態
                    resolve(res.result)//res.message為參數A
                } else {
                    // reject()異步回調將promise變為rejected(已失敗)狀態
                    reject(res.result)//res.desc為參數B
                }
            }
        });
    })
    // 每個Promise的實例對象,都有一個then的方法,這個方法就是用來處理之前各種異步邏輯的結果(fulfilled(已成功)狀態或者rejected(已失敗)狀態)
    // 第一個回調函數是Promise對象的狀態變為resolved時調用,
    // 第二個回調函數是Promise對象的狀態變為rejected時調用。其中,第二個函數是可選的,不一定要提供。這兩個函數都接受Promise對象傳出的值作為參數。
    .then(res => { 
        console.log('res參數來自resolve(A)傳入的參數A',res)
    },err =>{
        console.log('reject(B)傳入的參數B',err)
    })
    console.log(a)
        
// 如果異步操作獲得了我們想要的結果,那我們將調用resolve函數,在then的第一個作為參數的匿名函數中可以獲取數據,如果我們得到了錯誤的結果,調用reject函數,
// 在then函數的第二個作為參數的匿名函數中獲取錯誤處理數據。
</script>

</body>
</html>

  


當res.code == 0 為接口調用成功 輸出:

手動把 上面代碼 res.code == 0 改為 res.code == 1 就會抱一個錯誤,輸出:

如果異步操作獲得了我們想要的結果,那我們將調用resolve函數,在then的第一個作為參數的匿名函數中可以獲取數據,如果我們得到了錯誤的結果,調用reject函數,在then函數的第二個作為參數的匿名函數中獲取錯誤處理數據。
這樣,一個次完整的Promise調用就結束了。對於Promise的then()方法,then總是會返回一個Promise實例,因此你可以一直調用then,形如run().then().then().then().then().then()…
在一個then()方法調用異步處理成功的狀態時,你既可以return一個確定的“值”,也可以再次返回一個Promise實例,當返回的是一個確切的值的時候,then會將這個確切的值傳入一個默認的Promise實例,並且這個Promise實例會立即置為fulfilled狀態,以供接下來的then方法里使用。看代碼:

let num = 0 let run = function() { return new Promise(resolve => { resolve(`${num}`)}) } run().then(val => { console.log(val) return val }) .then(val =>{ val++ console.log(val) return val }) .then(val =>{ val++ console.log(val) })

 

輸出:

根據這個特性,我們就可以將相互依賴的多個異步邏輯,進行比較順序的管理了,解決了讓人頭痛的回調地獄問題。

今天我們就先到這里,明天我們講一下Promise.then()與Promise.catch()

友情鏈接:點擊查看所有文章目錄

友情鏈接:點擊查看 JavaScript異步系列文章目錄

原文鏈接:https://blog.csdn.net/qq_42911663/article/details/85790181


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM