Promise初體驗

Promise是什麼

JS就是操做對象上的屬性和方法,對於一個對象,想要了解,咱們能夠直接從其身上的屬性和方法入手;直接使用console.dir(對象)打印出來數組

Promise對象

從上面打印出來的屬性和方法,能夠看到Promise是一個構造函數,有屬於本身私有的all,reject,resolve,rece等方法,也有原型上面的,屬於實例對象調用的方法then,catch瀏覽器

// Promise裏面傳入一個函數類型的參數,這個函數類型的參數接收兩個參數resolve reject
var p=new Promise(function(resolve,reject){
     // 異步操做
     setTimeout(function(){
         console.log('icessun');  // 兩秒以後打印出icessun
         resolve('icessun2'); // resolve是成功後的回調函數 裏面的icessun2是傳入的參數
      },2000)
  });
// 那麼p是一個實例對象,可使用then方法(Promise原型上面的方法)
p.then(function(){
    console.log(arguments);  // 會打印出一個類數組 ['icessun2']
    
 })
p.then(function(data){
    console.log(data);  // 會打印出icessun2 data接收了resolve裏面的參數
 })

複製代碼

對於上面這段代碼,首先new一個實例對象賦值給pPromise的構造函數接受一個參數,是函數;而且傳入兩個參數:resolve,reject,分別表示異步操做執行成功後的回調函數和異步操做執行失敗後的回調函數;而後裏面設置一個定時器setTimeout,開啓一個異步操做,兩秒後輸出icessun,而且調用resolve方法,注意一個細節bash

上面的代碼,只是new了一個對象實例,並無調用,就執行了;對於這個狀況,通常是把其嵌套在一個函數裏面,避免當即執行,在須要的時候去運行這個函數。dom

p 是一個實例對象,可使用then方法,其裏面的函數是對於resolve或者reject的調用的體現,能夠接收resolve,reject傳入的參數異步

function icessun(){
   var p=new Promise(function(resolve,reject){
        setTimeout(function(){
           console.log('icessun');
           reslove('icessun2');
        },2000);
    });
  return p; // 返回p實例,使其可使用Promise原型上面的方法
}
icessun(); // 調用執行icessun函數 獲得一個Promis對象

// 也能夠直接這樣調用
icessun().then(function(data){
console.log(data); // icessun2
// 一些其餘的操做
// .....
});
複製代碼

經過上面的代碼,知道then裏面的函數就是常常說的回調函數callback,在icessun這個異步任務執行完成後被執行。把回調函數寫法分離出來,在異步操做執行完後,用鏈式調用的方法執行回調函數,對於多層回調來講,很是的方便,能夠繼續在then的方法中繼續寫Promise對象並返回,繼續調用then來進行回調操做,這就是Promise的做用。函數

鏈式操做

從上面看,Promise對於多層回調,能夠簡化其寫法,使得更加的語義化;可是Promise的精髓在於其鏈式操做,傳遞狀態,維護狀態的方式使得回調函數可以及時的調用。打開Promise的正確場景是這樣:ui

function runAsync1(){
  var p=new Promise(function(resolve,reject){
        setTimeout(function(){
            console.log('執行完成1')
            resolve('icessun1');
         },2000);
   });

   return p; // 返回p實例對象
}
function runAsync2(){
  var p=new Promise(function(resolve,reject){
        setTimeout(function(){
            console.log('執行完成2')
            resolve('icessun2');
         },2000);
   });

   return p; // 返回p實例對象
}
function runAsync3(){
  var p=new Promise(function(resolve,reject){
        setTimeout(function(){
            console.log('執行完成3')
            resolve('icessun3');
         },2000);
   });

   return p; // 返回p實例對象
}

// 正確的打開Promise的方法
runAsync1()
           .then(function(data){
               console.log(data);
               return runAsync2();
           })
           .then(function(data){
                console.log(data);
                return runAsync3();
            })
            .then(function(data){
                console.log(data);
             })
複製代碼

這樣可以按照順序,每隔兩秒輸出每一個異步回調中的內容,運行結果:spa

鏈式操做1
固然咱們能夠直接 return數據而不是 Promise對象,在後面的 then方法就能夠直接接收到數據,以下:

// 正確的打開Promise的方法
runAsync1()
           .then(function(data){
               console.log(data);
               return runAsync2();
           })
           .then(function(data){
                console.log(data);
                return '我是直接返回的數據';
            })
            .then(function(data){
                console.log(data);
             })
複製代碼

鏈式操做2

reject的用法

前面咱們說了resolve是執行成功的回調,那麼reject就是執行失敗的回調,將Promise的狀態設置爲rejected,這樣就能夠在then裏面獲取到,執行失敗狀況下的回調。code

function getNumber(){
   var p=new Promise(function(resolve,reject){
      setTimeout(function(){
          var num=Math.ceil(Math.random()*10); // 生成1-10 之間的隨機數 Math.ceil(): 大於或等於給定數字的最小整數
          if(num<=5){
            resolve(num);
           }else{
             reject('數字太大了')
            }
        },2000);
    });
   return p;
}

getNumber()
          .then(function(data){
               console.log('resolved');
               console.log(data);
            },function(reason,data){
                  console.log('resolved');
               console.log(reason); // 數字太大
                console.log(data); // undefined
              });
複製代碼

getNumber()函數執行後會出現兩種狀況,要麼大於5,要麼小於5,在then中傳入了兩個參數,第一個是對應resolve的回調,第二個是對應reject的回調。cdn

catch的用法

看到這個方法,就會想到瀏覽器處理異常的try...catch()方法,有錯誤進入catch方法,不阻斷程序的執行,其實這個方法也是來處理錯誤的,用來指定reject的回調,防止程序錯誤,阻斷後面程序的執行,使其可以繼續執行。

getNumber()
           .then(function(data){
              console.log('resolve');
              console.log(data);
            })
            .catch(function(data){
               console.log('reject');
               console.log(data);
             })
複製代碼

其效果和上面在then裏面寫兩個函數是同樣的,這個寫法的好處是當代碼出現了錯誤的時候,不會阻斷程序的執行,而是進入catach方法。

all方法的使用

Promise對象上的方法,實例不能使用,只能這個對象使用,這個方法經過了並行執行異步操做的能力,而且在全部的異步操做完成後才執行回調

Promise
       .all([runAsync1(),runAsync2(),runAsync3()])
       .then(function(results){
          console.log(results);
        });
複製代碼

Promise.all來執行前面的三個異步的函數,all()接收一個數組參數,裏面的執行最終都返回Promise對象,只有等三個異步操做都執行完成後纔會進入到then裏面,all會把全部的異步操做的結果放在一個數組中傳給then,就是上面的results,代碼的輸出結果:

all執行結果

有了all,能夠並行執行多個異步操做,而且在一個回調中處理全部的返回數據,一個經常使用的場景:遊戲類的素材比較多的應用,打開網頁的時候,預先加載須要用到的各類資源,如圖片,flash以及各類靜態文件,等到全部都加載完成,咱們再進行頁面的初始化。

race的用法

這個也是Promise類上面的私有方法,對於前面的all方法來講是:誰的程序執行的慢,就等誰執行完纔回調。可是對於race來講:誰的程序執行的快,就以它爲標準調用回調函數,其用法基本上是同樣的,把上面runAsync1函數的延遲改成1秒

Promise
      .race([runAsync1(),runAsync2(),runAsync3()])
       .then(function(results){
         console.log(results);
        });
複製代碼

這三個 異步操做一樣是並行執行的,可是等到1秒後,runAsync1已經執行完畢,因而then接受到了執行完畢的回調,輸出回調結果;與此同時,runAsyn2runAsyn3也繼續執行,輸出了執行的結果,可是不能回調then方法。

race的用法

這個方法的使用場景不少,好比能夠用race給某個異步請求設置超時時間,而且在超時後執行相應的操做:

// 請求某個圖片資源 異步
function requestImg(){
    var p=new Promise(function(resolve,reject){
        var img=new Image(); // 建立一個圖片對象實例 Image後面沒有參數的時候,括號能夠省略
        img.src='xxxxx'; // 給對象上面的屬性設置屬性值
        img.onload=function(){
           resolve(img); // 圖片成功加載的時候,把img對象做爲參數傳到回調函數裏面
         }
     });
   return p; // 當調用這個函數的時候可使用then方法
 }

 // 延時函數 給請求計時
 function timeout(){
    var p=new Promise(function(resolve,reject){
         setTimeout(function(){
            reject('圖片請求超時');
          },4000);
     });
     return p;
  } 

Promise.race([requsetImg(),timeout()])
       .then(function(results){
           console.log(results); // 圖片成功加載會把圖片的路徑打印在控制檯
        })
        .catch(function(reason){
         console.log(reason); // 失敗會提示加載失敗
         })

複製代碼

requestImg函數會異步請求一張圖片,圖片地址寫錯,確定是沒法加載圖片請求。timeout函數是一個延時4秒的異步操做,把這兩個返回Promise對象的函數放到race裏面,若是4秒內圖片請求成功,就會回調then方法,執行正常的流程,不然進入catch方法,顯示圖片請求超時。

請求超時的結果

正確的請求的結果
相關文章
相關標籤/搜索