ES6的Promise對象

Promise對象,ES6新增的一個全新特性,今天我們要好好學(xué)習一下它。
Promise的設(shè)計初衷

首先,我們先一起了解一下,為什么要設(shè)計出這么一個玩意兒,用它是為了解決什么問題?

    帶著這個問題,我們來回想一下日常開發(fā)中,經(jīng)常需要用到ajax請求數(shù)據(jù),拿到數(shù)據(jù)后,再進行一些處理。

    可有一次,你需要用ajax進行多次請求,而且,每次請求都依賴上一次請求返回的數(shù)據(jù)來作為參數(shù),然后繼續(xù)發(fā)出請求,你把代碼寫成了這樣:
//------請求A 開始---------
    $.ajax({
        success:function(res1){


            //------請求B 開始----
            $.ajax({
                success:function(res2){


                    //----請求C 開始---
                    $.ajax({
                        success:function(res3){
                        }
                    });
                    //---請求C 結(jié)束---


                }    
            });
            //------請求B 結(jié)束-----


        }
    });
    //------請求A 結(jié)束---------
    上面的案例,假設(shè)請求C需要依賴請求B返回的數(shù)據(jù),所以,C只能放在B的success函數(shù)內(nèi);B需要依賴A請求得到的數(shù)據(jù)作為參數(shù),所以,B只能放在A的success函數(shù)內(nèi);也就是:請求A包含著請求B,請求B又包含了請求C。

    就這樣,請求順序為:請求A -> 請求B -> 請求C,最后你也能順利的完成了任務(wù)。

傳統(tǒng)寫法的不足
但是這樣寫,會有一些缺點:

  1. 如果存在多個請求操作層層依賴的話,那么以上的嵌套就有可能不止三層那么少了,加上每一層還會有復(fù)雜的業(yè)務(wù)邏輯處理,代碼可讀性也越來越差,不直觀,調(diào)試起來也不方便。如果多人開發(fā)的時候沒有足夠的溝通協(xié)商,大家的代碼風格不一致的話,更是雪上加霜,給后面的維護帶來極大的不便。

  2. 如果請求C的需要依賴A和B的結(jié)果,但是請求A和B缺互相獨立,沒有依賴關(guān)系,以上的實現(xiàn)方式,就使得B必須得到A請求完成后才可以執(zhí)行,無疑是消耗了更多的等待時間。

    既然使用這種回調(diào)函數(shù)層層嵌套(又稱:回調(diào)地獄)的形式存在缺點,ES6想了辦法治它,所以就有了Promise的出現(xiàn)了。

    那么我們就知道了:Promise對象能使我們更合理、更規(guī)范地進行處理異步操作。

Promise的基本用法
接下來,我們就看看它的基本用法:

    let pro = new Promise(function(resolve,reject){
        //....
    });
Promise對象是全局對象,你也可以理解為一個類,創(chuàng)建Promise實例的時候,要有那個new關(guān)鍵字。參數(shù)是一個匿名函數(shù),其中有兩個參數(shù):resolve和reject,兩個函數(shù)均為方法。resolve方法用于處理異步操作成功后業(yè)務(wù);reject方法用于操作異步操作失敗后的業(yè)務(wù)。

Promise的是三種狀態(tài)
Promise對象有三種狀態(tài):
pending:剛剛創(chuàng)建一個Promise實例的時候,表示初始狀態(tài);
fulfilled:resolve方法調(diào)用的時候,表示操作成功;
rejected:reject方法調(diào)用的時候,表示操作失??;

    狀態(tài)只能從 初始化 -> 成功  或者  初始化 -> 失敗,不能逆向轉(zhuǎn)換,也不能在成功fulfilled 和失敗rejected之間轉(zhuǎn)換。
    let pro = new Promise(function(resolve,reject){
        //實例化后狀態(tài):pending

        if('操作成功'){
            resolve();
            //resolve方法調(diào)用,狀態(tài)為:fulfilled
        }else{
            reject();
            //reject方法調(diào)用,狀態(tài)為:rejected
        }
    });
    

上面的注釋,講清楚了一個Promise實例的狀態(tài)改變情況。

    初始化實例后,對象的狀態(tài)就變成了pending;當resolve方法被調(diào)用的時候,狀態(tài)就變成了:成功fulfilled;當reject方法被調(diào)用的時候,狀態(tài)就會有pending變成失敗rejected。

then( )方法
了解了Promise的創(chuàng)建和狀態(tài),我們來學(xué)習一個最重要的實例方法:then( )方法。

    then( )方法:用于綁定處理操作后的處理程序。
    pro.then(function (res) {
        //操作成功的處理程序
    },function (error) {
        //操作失敗的處理程序
    });
    

參數(shù)是兩個函數(shù),第一個用于處理操作成功后的業(yè)務(wù),第二個用于處理操作異常后的業(yè)務(wù)。

catch( )方法

對于操作異常的程序,Promise專門提供了一個實例方法來處理:catch( )方法。

   pro.catch(function (error) {
        //操作失敗的處理程序
    });
    

catch只接受一個參數(shù),用于處理操作異常后的業(yè)務(wù)。

    綜合上面的兩個方法,大家都建議將then方法用于處理操作成功,catch方法用于處理操作異常,也就是:
   pro.then(function (res) {
        //操作成功的處理程序
    }).catch(function (error) {
        //操作失敗的處理程序
    });

之所以能夠使用鏈式調(diào)用,是因為then方法和catch方法調(diào)用后,都會返回promise對象。

    講了那么多,如果你之前一點都沒接觸過Promise的話,現(xiàn)在一定很懵逼,沒關(guān)系,下面我們用一個案例來串聯(lián)前面的知識點,演示一下,認真閱讀注釋:
 //用new關(guān)鍵字創(chuàng)建一個Promise實例
    let pro = new Promise(function(resolve,reject){
        //假設(shè)condition的值為true
        let condition = true;

        if(condition){
            //調(diào)用操作成功方法
            resolve('操作成功');
            //狀態(tài):pending->fulfilled
        }else{
            //調(diào)用操作異常方法
            reject('操作異常');
            //狀態(tài):pending->rejected
        }
    });

    //用then處理操作成功,catch處理操作異常
    pro.then(function (res) {

        //操作成功的處理程序
        console.log(res)

    }).catch(function (error) {

        //操作失敗的處理程序
        console.log(error)

    });
    //控制臺輸出:操作成功

上面案例的注釋十分詳細,串聯(lián)起了上面介紹的所有知識點:創(chuàng)建實例,狀態(tài)轉(zhuǎn)換,then方法和catch方法的使用。

    由于我們設(shè)置了變量condition的值為true,所以執(zhí)行后控制臺輸出的結(jié)果是:“操作成功”。

    上面就是Promise用于處理操作異常的這個過程;但是,正如文章開頭講到的,如果多個操作之間層層依賴,我們用Promise又是怎么處理的呢?

完整案例
我們看看下面的案例,代碼有點長,但是一點都不復(fù)雜:

let pro = new Promise(function(resolve,reject){

        if(true){
            //調(diào)用操作成功方法
            resolve('操作成功');
        }else{
            //調(diào)用操作異常方法
            reject('操作異常');
        }
    });

//用then處理操作成功,catch處理操作異常
    pro.then(requestA)
        .then(requestB)
        .then(requestC)
        .catch(requestError);

    function requestA(){
        console.log('請求A成功');
        return '請求B,下一個就是你了';
    }
    function requestB(res){
        console.log('上一步的結(jié)果:'+res);
        console.log('請求B成功');
        return '請求C,下一個就是你了';
    }
    function requestC(res){
        console.log('上一步的結(jié)果:'+res);
        console.log('請求C成功');
    }
    function requestError(){
        console.log('請求失敗');
    }

    //打印結(jié)果:
    //請求A成功
    //上一步的結(jié)果:請求B,下一個就是你了
    //請求B成功
    //上一步的結(jié)果:請求C,下一個就是你了
    //請求C成功

案例中,先是創(chuàng)建一個實例,還聲明了4個函數(shù),其中三個是分別代表著請求A,請求B,請求C;有了then方法,三個請求操作再也不用層層嵌套了。我們使用then方法,按照調(diào)用順序,很直觀地完成了三個操作的綁定,并且,如果請求B依賴于請求A的結(jié)果,那么,可以在請求A的程序用使用return語句把需要的數(shù)據(jù)作為參數(shù),傳遞給下一個請求,案例中我們就是使用return實現(xiàn)傳遞參數(shù)給下一步操作的。
更直觀的圖解
如果你還是是懂非懂,沒關(guān)系,前端君拼了,上圖:


TIM截圖20180109092942.jpg

圖有點粗糙,但是能反應(yīng)出上面程序的執(zhí)行過程,幫助大家加深理解。

    除了提供了實例方法以外,Promise還提供了一些類方法,也就是不用創(chuàng)建實例,也可以調(diào)用的方法。

    下面,我們來學(xué)習幾個重要的。

Promise.all( )方法
Promise.all( )方法:接受一個數(shù)組作為參數(shù),數(shù)組的元素是Promise實例對象,當參數(shù)中的實例對象的狀態(tài)都為fulfilled時,Promise.all( )才會有返回。

    我們來演示一下:
//創(chuàng)建實例pro1
    let pro1 = new Promise(function(resolve){
        setTimeout(function () {
            resolve('實例1操作成功');
        },5000);
    });
    
    //創(chuàng)建實例pro2
    let pro2 = new Promise(function(resolve){
        setTimeout(function () {
            resolve('實例2操作成功');
        },1000);
    });

    
    Promise.all([pro1,pro2]).then(function(result){
        console.log(result);
    });
    //打印結(jié)果:["實例1操作成功", "實例2操作成功"]

上述案例,我們創(chuàng)建了兩個Promise實例:pro1和pro2,我們注意兩個setTimeout的第二個參數(shù),分別是5000毫秒和1000毫秒,當我們調(diào)用Promise.all( )方法的時候,會延遲到5秒才控制臺會輸出結(jié)果。

    因為1000毫秒以后,實例pro2進入了成功fulfilled狀態(tài);此時,Promise.all( )還不會有所行動,因為實例pro1還沒有進入成功fulfilled狀態(tài);等到了5000毫秒以后,實例pro1也進入了成功fulfilled狀態(tài),Promise.all( )才會進入then方法,然后在控制臺輸出:["實例1操作成功","實例2操作成功"]。

    這個方法有什么用呢?一般這樣的場景:我們執(zhí)行某個操作,這個操作需要得到需要多個接口請求回來的數(shù)據(jù)來支持,但是這些接口請求之前互不依賴,不需要層層嵌套。這種情況下就適合使用Promise.all( )方法,因為它會得到所有接口都請求成功了,才會進行操作。

Promise.race( )方法
另一個類似的方法是Promise.race()方法:它的參數(shù)要求跟Promise.all( )方法一樣,不同的是,它參數(shù)中的promise實例,只要有一個狀態(tài)發(fā)生變化(不管是成功fulfilled還是異常rejected),它就會有返回,其他實例中再發(fā)生變化,它也不管了。

//初始化實例pro1
    let pro1 = new Promise(function(resolve){
        setTimeout(function () {
            resolve('實例1操作成功');
        },4000);
    });

    //初始化實例pro2
    let pro2 = new Promise(function(resolve,reject){
        setTimeout(function () {
            reject('實例2操作失敗');
        },2000);
    });

    Promise.race([pro2,pro1]).then(function(result){
        console.log(result);
    }).catch(function(error){
        console.log(error);
    });
    //打印結(jié)果:實例2操作失敗

同樣是兩個實例,實例pro1不變,不同的是實例pro2,這次我們調(diào)用的是失敗函數(shù)reject。

    由于pro2實例中2000毫秒之后就執(zhí)行reject方法,早于實例pro1的4000毫秒,所以最后輸出的是:實例2操作失敗。

    以上就是對Promise對象的內(nèi)容講解,上面提到了一個概念:回調(diào)地獄;指的是過多地使用回調(diào)函數(shù)嵌套,使得調(diào)試和維護起來極其的不便。

總結(jié):Promise是一個讓開發(fā)者更合理、更規(guī)范地用于處理異步操作的對象,它有三種狀態(tài):初始化、操作成功、操作異常。使用實例方法:then( ) 和 catch( ) 來綁定處理程序;還提供了類方法:Promise.all( ) 和 Promise.race( )。
摘抄自前端君教程-->
http://mp.weixin.qq.com/s/jUdSRsF6fBik1ZyVSLAk-g

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Promise的含義: ??Promise是異步編程的一種解決方案,比傳統(tǒng)的解決方案——回調(diào)函數(shù)和事件——更合理和...
    呼呼哥閱讀 2,281評論 0 16
  • 相信凡是寫過javascript的童鞋也一定都寫過回調(diào)方法(callback),簡單說回調(diào)方法就是將一個方法fun...
    ac68882199a1閱讀 86,578評論 12 90
  • Promiese 簡單說就是一個容器,里面保存著某個未來才會結(jié)束的事件(通常是一個異步操作)的結(jié)果,語法上說,Pr...
    雨飛飛雨閱讀 3,492評論 0 19
  • 00、前言Promise 是異步編程的一種解決方案,比傳統(tǒng)的解決方案——回調(diào)函數(shù)和事件——更合理和更強大。它由社區(qū)...
    夜幕小草閱讀 2,230評論 0 12
  • 我是一名高職院校的專業(yè)課老師,按說老師應(yīng)該口才很好,而我除了上課,在其他公共場合會有演講恐懼癥,不知道如何說,...
    心的獨白_閱讀 292評論 3 3

友情鏈接更多精彩內(nèi)容