//------請求A 開始--------- $.ajax({ success:function(res1){ //------請求B 開始---- $.ajax({ success:function(res2){ //----請求C 開始--- $.ajax({ success:function(res3){ } }); //---請求C 結束--- } }); //------請求B 結束----- } }); //------請求A 結束--------- 上面的案例,假設請求C須要依賴請求B返回的數據,因此,C只能放在B的success函數內;B須要依賴A請求獲得的數據做爲參數,因此,B只能放在A的success函數內;也就是:請求A包含着請求B,請求B又包含了請求C。 就這樣,請求順序爲:請求A -> 請求B -> 請求C,最後你也能順利的完成了任務。 傳統寫法的不足 可是這樣寫,會有一些缺點: 1. 若是存在多個請求操做層層依賴的話,那麼以上的嵌套就有可能不止三層那麼少了,加上每一層還會有複雜的業務邏輯處理,代碼可讀性也愈來愈差,不直觀,調試起來也不方便。若是多人開發的時候沒有足夠的溝通協商,你們的代碼風格不一致的話,更是雪上加霜,給後面的維護帶來極大的不便。 2. 若是請求C的須要依賴A和B的結果,可是請求A和B缺互相獨立,沒有依賴關係,以上的實現方式,就使得B必須獲得A請求完成後才能夠執行,無疑是消耗了更多的等待時間。 既然使用這種回調函數層層嵌套(又稱:回調地獄)的形式存在缺點,ES6想了辦法治它,因此就有了Promise的出現了。 那麼咱們就知道了:Promise對象能使咱們更合理、更規範地進行處理異步操做。 Promise的基本用法 接下來,咱們就看看它的基本用法: let pro = new Promise(function(resolve,reject){ //.... }); Promise對象是全局對象,你也能夠理解爲一個類,建立Promise實例的時候,要有那個new關鍵字。參數是一個匿名函數,其中有兩個參數:resolve和reject,兩個函數均爲方法。resolve方法用於處理異步操做成功後業務;reject方法用於操做異步操做失敗後的業務。 Promise的是三種狀態 Promise對象有三種狀態: pending:剛剛建立一個Promise實例的時候,表示初始狀態; fulfilled:resolve方法調用的時候,表示操做成功; rejected:reject方法調用的時候,表示操做失敗; 狀態只能從 初始化 -> 成功 或者 初始化 -> 失敗,不能逆向轉換,也不能在成功fulfilled 和失敗rejected之間轉換。 let pro = new Promise(function(resolve,reject){ //實例化後狀態:pending if('操做成功'){ resolve(); //resolve方法調用,狀態爲:fulfilled }else{ reject(); //reject方法調用,狀態爲:rejected } }); 上面的註釋,講清楚了一個Promise實例的狀態改變狀況。 初始化實例後,對象的狀態就變成了pending;當resolve方法被調用的時候,狀態就變成了:成功fulfilled;當reject方法被調用的時候,狀態就會有pending變成失敗rejected。 then( )方法 瞭解了Promise的建立和狀態,咱們來學習一個最重要的實例方法:then( )方法。 then( )方法:用於綁定處理操做後的處理程序。 pro.then(function (res) { //操做成功的處理程序 },function (error) { //操做失敗的處理程序 }); 參數是兩個函數,第一個用於處理操做成功後的業務,第二個用於處理操做異常後的業務。 catch( )方法 對於操做異常的程序,Promise專門提供了一個實例方法來處理:catch( )方法。 pro.catch(function (error) { //操做失敗的處理程序 }); catch只接受一個參數,用於處理操做異常後的業務。 綜合上面的兩個方法,你們都建議將then方法用於處理操做成功,catch方法用於處理操做異常,也就是: pro.then(function (res) { //操做成功的處理程序 }).catch(function (error) { //操做失敗的處理程序 }); 之因此可以使用鏈式調用,是由於then方法和catch方法調用後,都會返回promise對象。 講了那麼多,若是你以前一點都沒接觸過Promise的話,如今必定很懵逼,不要緊,下面咱們用一個案例來串聯前面的知識點,演示一下,認真閱讀註釋: //用new關鍵字建立一個Promise實例 let pro = new Promise(function(resolve,reject){ //假設condition的值爲true let condition = true; if(condition){ //調用操做成功方法 resolve('操做成功'); //狀態:pending->fulfilled }else{ //調用操做異常方法 reject('操做異常'); //狀態:pending->rejected } }); //用then處理操做成功,catch處理操做異常 pro.then(function (res) { //操做成功的處理程序 console.log(res) }).catch(function (error) { //操做失敗的處理程序 console.log(error) }); //控制檯輸出:操做成功 上面案例的註釋十分詳細,串聯起了上面介紹的全部知識點:建立實例,狀態轉換,then方法和catch方法的使用。 因爲咱們設置了變量condition的值爲true,因此執行後控制檯輸出的結果是:「操做成功」。 上面就是Promise用於處理操做異常的這個過程;可是,正如文章開頭講到的,若是多個操做之間層層依賴,咱們用Promise又是怎麼處理的呢? 完整案例 咱們看看下面的案例,代碼有點長,可是一點都不復雜: let pro = new Promise(function(resolve,reject){ if(true){ //調用操做成功方法 resolve('操做成功'); }else{ //調用操做異常方法 reject('操做異常'); } }); //用then處理操做成功,catch處理操做異常 pro.then(requestA) .then(requestB) .then(requestC) .catch(requestError); function requestA(){ console.log('請求A成功'); return '請求B,下一個就是你了'; } function requestB(res){ console.log('上一步的結果:'+res); console.log('請求B成功'); return '請求C,下一個就是你了'; } function requestC(res){ console.log('上一步的結果:'+res); console.log('請求C成功'); } function requestError(){ console.log('請求失敗'); } //打印結果: //請求A成功 //上一步的結果:請求B,下一個就是你了 //請求B成功 //上一步的結果:請求C,下一個就是你了 //請求C成功 案例中,先是建立一個實例,還聲明瞭4個函數,其中三個是分別表明着請求A,請求B,請求C;有了then方法,三個請求操做不再用層層嵌套了。咱們使用then方法,按照調用順序,很直觀地完成了三個操做的綁定,而且,若是請求B依賴於請求A的結果,那麼,能夠在請求A的程序用使用return語句把須要的數據做爲參數,傳遞給下一個請求,案例中咱們就是使用return實現傳遞參數給下一步操做的。 更直觀的圖解 若是你仍是是懂非懂,不要緊,前端君拼了,上圖: 圖有點粗糙,可是能反應出上面程序的執行過程,幫助你們加深理解。 除了提供了實例方法之外,Promise還提供了一些類方法,也就是不用建立實例,也能夠調用的方法。 下面,咱們來學習幾個重要的。 Promise.all( )方法 Promise.all( )方法:接受一個數組做爲參數,數組的元素是Promise實例對象,當參數中的實例對象的狀態都爲fulfilled時,Promise.all( )纔會有返回。 咱們來演示一下: //建立實例pro1 let pro1 = new Promise(function(resolve){ setTimeout(function () { resolve('實例1操做成功'); },5000); }); //建立實例pro2 let pro2 = new Promise(function(resolve){ setTimeout(function () { resolve('實例2操做成功'); },1000); }); Promise.all([pro1,pro2]).then(function(result){ console.log(result); }); //打印結果:["實例1操做成功", "實例2操做成功"] 上述案例,咱們建立了兩個Promise實例:pro1和pro2,咱們注意兩個setTimeout的第二個參數,分別是5000毫秒和1000毫秒,當咱們調用Promise.all( )方法的時候,會延遲到5秒才控制檯會輸出結果。 由於1000毫秒之後,實例pro2進入了成功fulfilled狀態;此時,Promise.all( )還不會有所行動,由於實例pro1尚未進入成功fulfilled狀態;等到了5000毫秒之後,實例pro1也進入了成功fulfilled狀態,Promise.all( )纔會進入then方法,而後在控制檯輸出:["實例1操做成功","實例2操做成功"]。 這個方法有什麼用呢?通常這樣的場景:咱們執行某個操做,這個操做須要獲得須要多個接口請求回來的數據來支持,可是這些接口請求以前互不依賴,不須要層層嵌套。這種狀況下就適合使用Promise.all( )方法,由於它會獲得全部接口都請求成功了,纔會進行操做。 Promise.race( )方法 另外一個相似的方法是Promise.race()方法:它的參數要求跟Promise.all( )方法同樣,不一樣的是,它參數中的promise實例,只要有一個狀態發生變化(不論是成功fulfilled仍是異常rejected),它就會有返回,其餘實例中再發生變化,它也無論了。 //初始化實例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); }); //打印結果:實例2操做失敗 一樣是兩個實例,實例pro1不變,不一樣的是實例pro2,此次咱們調用的是失敗函數reject。 因爲pro2實例中2000毫秒以後就執行reject方法,早於實例pro1的4000毫秒,因此最後輸出的是:實例2操做失敗。