【前端】主流API-promise解析,js基礎。


前言

  在js領域,promise出現的時間已經很久了,從jquery的$.get().done().fail() 這樣的API開始,到現在的es6默認支持的new Promise(),它的出現無疑使異步代碼變得信任可靠,更使得前端代碼的信任度直線提升,就跟它的名字一樣,它的到來,是一個 Promise (保證,允諾)

異步信任問題

  有一個朋友在銀行系統工作,有一次,我幫他review一段代碼,據他所說,這段代碼給他帶來了一個很大的麻煩,有一位顧客使用這個頁面付款的時候意外的提交了兩次付款請求,boss復查發現提交的源頭是前端,當我接到這段代碼的時候,這位朋友已經焦頭爛額,我接過了代碼, 初步分析,什么也沒發現,他的代碼就像下面這樣:

$("btn").on("click",function(){
                    if($(this).hasClass("no-submit")){
                        $(this).removeClass("no-submit");
                        //忽略
                        $.ajax({
                            url:"....",
                            type:"json",
                            success:function(){
                                submit();
                            }
                        })
                    }
                })

他告訴我,他已經針對按鈕做點擊唯一提交了,怎么可能調用兩次?

但是事實就是有兩次submit。

通過分析代碼,最后排查的結果,原因是這個jquery的$.ajax是被二次封裝過的,某種情況下回調函數被調用了兩次。

oh,看起來很簡單的原因,這可是個巨坑! 日常代碼中,我們都需要接觸各種各樣的第三方庫,誰來保證它們都沒有隱藏的問題?如果你遇到了經過加密混淆的庫,因為它內部某個try catch,或者內部某個if在非正常情況下發成的誤判導致重復調用,我想你就不會再淡定的喝着咖啡敲代碼了。

或者你覺得,防止重復調用這個很簡單,加個判斷就行了:

var a = 0;
                $("btn").on("click",function(){
                    if($(this).hasClass("no-submit")){
                        $(this).removeClass("no-submit");
                        //忽略
                        $.ajax({
                            url:"....",
                            type:"json",
                            success:function(){
                                if( a==0){
                                    submit();
                                }
                                
                            }
                        })
                    }
                })

完美是吧,測試上線,繼續喝咖啡。

三個月后。。。。。

有些顧客反應,點擊提交后,有時沒有提交成功,老板這時大發雷霆,這已經是第二次出現這樣的失誤了。

是的,只調用一次,但是鬼知道它會不會調用一次?

這里問題的本質是什么?

它就是所謂的回調地域。

我們總是有很多的回調函數需要交給第三方工具處理,類似於$.ajax,jquery就一定是值得信任的?圖樣圖森破!你永遠不知道jquery下面可能還埋着上一位開發者給你准備的周年大禮包。最糟糕的是,這個第三方工具你沒有修改的能力,要么你換掉他,要么你解密,要么換版本,誰知道作者會不會繼續更新?

把我們的回調交給不受信任的第三方程序,這就是回調地域,可怕的很!

Promise調用方式

  今天的主角是promise。它可以讓我們的回調變得promise。 

試想,我們把上述的ajax再次封裝(在不能改動源代碼的情況下),可能代碼如下:

 var then = function(resolve,reject,timeout){
                var a = 0;
                $("btn").on("click",function(){
                    if($(this).hasClass("no-submit")){
                        $(this).removeClass("no-submit");
                        //忽略
                        $.ajax({
                            url:"....",
                            type:"json",
                            success:function(){
                                a==0&&resolve();
                                a++;
                            },
                            error:function(){
                               a==0&&reject();
                               a++;
                            }
                        })
                    }
                })
                if(new Date.now()>timeout){
                   a==0&&reject();
                   a++;
                }
            }
            var a = 0;

ok,我只是將前面的代碼封裝到一個叫then的函數里,這個函數需要三個參數,resolve(解決),reject(拒絕),以及一個超時時間,另外控制調用次數的變量a也有保留。

有什么不同?

不同之處在於,then函數是可信任的,至少現在我們可以確定,它要么被resolve,要么reject,絕不可能出現調用兩次,或者不調用的情況。這種封裝可以叫做控制反轉,

我們將原本給到$.ajax的回調權再次交回自己手中,上面的代碼調用起來是這樣的:

then(function(){
    submit()
},function(){
    error()
})

ok,我們只解決了其中的一些問題,重復調用,不調用,但還有更多的問題。

讓promise登場吧!

使用promise封裝上面代碼:

 function request(){
            return new Promise(function(resolve,reject){
                $.ajax({
                    url:"....",
                    type:"json",
                    success:function(){
                        resolve()
                    },
                    error:function(){
                       reject()
                    }
                })
            })
        }
       request.then(function(){
            submit();
       },function(){
            error();
       })

ok,是否豁然開朗?

promise就是解決關於異步回調信任,順便緩解了回調金字塔(一堆回調嵌套在一起,就是一個回調金字塔)問題的解決方案。

在這個Promise構造函數中傳入一個函數,這個函數有兩個值,resolve、reject。一旦代碼請求成功,resolve被調用,決議完成並忽略后面的所有調用。或者失敗后決議被拒絕,調用reject。

決議、解決、拒絕術語

  一個promise通常有三種狀態  pending(進行中)、fulfilled(已成功)和rejected(已失敗)。

可以說在任何情況下,一個promise只有一個決議。 它只可能是fulfilled或者rejected其中一種,並且是不可以逆轉的。意思是你無法讓一個已經被拒絕的promise回滾。它永遠都會有一個確定的回復。

   

Promise API

  promise.all(Array);

   Promise.all方法用於將多個 Promise 實例,包裝成一個新的 Promise 實例,並且這個新的promise返回的最終值是傳入的多個Promise決議完成的值。

   有了Promise.all方法,就可以將異步操作以同步操作的流程表達出來,避免了層層嵌套的回調函數。此外,Promise對象提供統一的接口,使得控制異步操作更加簡單易懂。

    像這樣的調用:

 Promise.all([request("url1"),request("url2")]).then((result)=>{
      console.log(result) //[url1result,url2result]
})

    注意傳入的順序,數組promise決議的值與返回的result數組位置是一致的。對於以往的回調方式來說,這是很強大的api。

  

  Promise.race(array)

    這個方法和all調用方式一樣,但是決議的方式不一樣,.all等待所有決議完成新的 Promise返回決議。 而.race,一旦有某個promise決議完成,其余的會立刻終止。也就是說它只會執行最快的那個promise。

   

     ...更多api可以查看阮大大的es6教程

結束

  promise已經成為主流。我在這里只講了一些淺見,至於更多的用法,與君共勉!

 


免責聲明!

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



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