Promise
是什麼JS
就是操做對象上的屬性和方法,對於一個對象,想要了解,咱們能夠直接從其身上的屬性和方法入手;直接使用console.dir(對象)
打印出來
從上面打印出來的屬性和方法,能夠看到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
一個實例對象賦值給p
,Promise
的構造函數接受一個參數,是函數;而且傳入兩個參數:resolve,reject
,分別表示異步操做執行成功後的回調函數和異步操做執行失敗後的回調函數;而後裏面設置一個定時器setTimeout
,開啓一個異步操做,兩秒後輸出icessun
,而且調用resolve
方法,注意一個細節
:瀏覽器
上面的代碼,只是
new
了一個對象實例,並無調用,就執行了;對於這個狀況,通常是把其嵌套在一個函數裏面,避免當即執行,在須要的時候去運行這個函數。
p
是一個實例對象,可使用then
方法,其裏面的函數是對於resolve
或者reject
的調用的體現,能夠接收resolve,reject
傳入的參數dom
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
的正確場景是這樣:
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); })
這樣可以按照順序,每隔兩秒輸出每一個異步回調中的內容,運行結果:函數
固然咱們能夠直接return
數據而不是Promise對象
,在後面的then
方法就能夠直接接收到數據,以下:spa
// 正確的打開Promise的方法 runAsync1() .then(function(data){ console.log(data); return runAsync2(); }) .then(function(data){ console.log(data); return '我是直接返回的數據'; }) .then(function(data){ console.log(data); })
reject
的用法前面咱們說了resolve
是執行成功的回調,那麼reject
就是執行失敗的回調,將Promise
的狀態設置爲rejected
,這樣就能夠在then
裏面獲取到,執行失敗狀況下的回調。
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
的回調。code
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
,能夠並行執行多個異步操做,而且在一個回調中處理全部的返回數據,一個經常使用的場景:遊戲類的素材比較多的應用,打開網頁的時候,預先加載須要用到的各類資源,如圖片,flash
以及各類靜態文件,等到全部都加載完成,咱們再進行頁面的初始化。圖片
race
的用法這個也是Promise
類上面的私有方法,對於前面的all
方法來講是:誰的程序執行的慢,就等誰執行完纔回調。可是對於race
來講:誰的程序執行的快,就以它爲標準調用回調函數,其用法基本上是同樣的,把上面runAsync1
函數的延遲改成1秒
Promise .race([runAsync1(),runAsync2(),runAsync3()]) .then(function(results){ console.log(results); });
這三個 異步操做一樣是並行執行的,可是等到1秒後,runAsync1
已經執行完畢,因而then
接受到了執行完畢的回調,輸出回調結果;與此同時,runAsyn2
和runAsyn3
也繼續執行,輸出了執行的結果,可是不能回調then
方法。
這個方法的使用場景不少,好比能夠用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
方法,顯示圖片請求超時。