博客地址:https://ainyi.com/16ajax
Promise,簡單說就是一個容器,裏面保存着某個將來纔會結束的事件(一般是一個異步操做)的結果。從語法上說,Promise 是一個對象,從它能夠獲取異步操做的消息。json
Promise 提供統一的 API,各類異步操做均可以用一樣的方法進行處理。api
Promise對象有如下兩個特色:數組
(1)對象的狀態不受外界影響。Promise對象表明一個異步操做,有三種狀態:Pending(進行中)、Resolved(已完成,又稱 Fulfilled)和Rejected(已失敗)。只有異步操做的結果,能夠決定當前是哪種狀態,任何其餘操做都沒法改變這個狀態。這也是Promise這個名字的由來,它的英語意思就是「承諾」,表示其餘手段沒法改變。promise
(2)一旦狀態改變,就不會再變,任什麼時候候均可以獲得這個結果。Promise對象的狀態改變,只有兩種可能:從Pending變爲Resolved和從Pending變爲Rejected。只要這兩種狀況發生,狀態就凝固了,不會再變了,會一直保持這個結果。就算改變已經發生了,你再對Promise對象添加回調函數,也會當即獲得這個結果。這與事件(Event)徹底不一樣,事件的特色是,若是你錯過了它,再去監聽,是得不到結果的。異步
有了Promise對象,就能夠將異步操做以同步操做的流程表達出來,避免了層層嵌套的回調函數。此外,Promise對象提供統一的接口,使得控制異步操做更加容易。函數
then 方法就是把原來的回調寫法分離出來,在異步操做執行完後,用鏈式調用的方式執行回調函數。工具
而 Promise 的優點就在於這個鏈式調用。咱們能夠在 then 方法中繼續寫 Promise 對象並返回,而後繼續調用 then 來進行回調操做。url
下面作一個買筆寫做業上交的演示,它們是層層依賴的關係,下一步的的操做須要使用上一部操做的結果。(這裏使用 setTimeout 模擬異步操做),正式開發能夠用 ajax 異步spa
1 //買筆 2 function buy(){ 3 console.log("開始買筆"); 4 var p = new Promise(function(resolve,reject){ 5 setTimeout(function(){ 6 console.log("買了筆芯"); 7 resolve("數學做業"); 8 },1000); 9 }); 10 return p; 11 } 12 //寫做業 13 function work(data){ 14 console.log("開始寫做業:"+data); 15 var p = new Promise(function(resolve,reject){ 16 setTimeout(function(){ 17 console.log("寫完做業"); 18 resolve("做業本"); 19 },1000); 20 }); 21 return p; 22 } 23 24 function out(data){ 25 console.log("開始上交:"+data); 26 var p = new Promise(function(resolve,reject){ 27 setTimeout(function(){ 28 console.log("上交完畢"); 29 resolve("得分:A"); 30 },1000); 31 }); 32 return p; 33 } 34 /* 不建議使用這種方式 35 buy().then(function(data){ 36 return work(data); 37 }).then(function(data){ 38 return out(data); 39 }).then(function(data){ 40 console.log(data); 41 });*/ 42 43 //推薦這種簡化的寫法 44 buy().then(work).then(out).then(function(data){ 45 console.log(data); 46 });
正式開發用ajax異步:
1 var promise = new Promise(function(resolve,reject){ 2 $.ajax({ 3 url:'/api/poisearch.json', 4 method:'get', 5 datatype:'json', 6 success:(res) =>{ 7 resolve(res) 8 }, 9 error:(err)=>{ 10 reject(err) 11 } 12 }); 13 }); 14 15 promise.then(function(res){ 16 return res.data 17 }).then(function(data){ 18 return data.result; 19 }).then(function(result){ 20 console.log(result) 21 }); 22 23 //推薦使用箭頭函數簡寫成,極大提高了代碼的簡潔性和可讀性 24 promise.then(res => res.data).then(data => data.result).then(result => console.log(result));
1 function rebuy(){ 2 console.log("開始買筆"); 3 var p = new Promise(function(resolve,reject){ 4 setTimeout(function(){ 5 console.log("買筆失敗"); 6 reject("沒帶夠錢"); 7 },1000); 8 }); 9 return p; 10 } 11 12 function rework(data){ 13 console.log("開始寫做業:"+data); 14 var p = new Promise(function(resolve,reject){ 15 setTimeout(function(){ 16 console.log("寫完做業"); 17 resolve("做業本"); 18 },1000); 19 }); 20 return p; 21 } 22 23 rebuy().then(rework,function(data){ 24 console.log(data); 25 });
1 function rebuy(){ 2 console.log("開始買筆"); 3 var p = new Promise(function(resolve,reject){ 4 setTimeout(function(){ 5 console.log("買筆失敗"); 6 reject("沒帶夠錢"); 7 },1000); 8 }); 9 return p; 10 } 11 12 function rework(data){ 13 console.log("開始寫做業:"+data); 14 var p = new Promise(function(resolve,reject){ 15 setTimeout(function(){ 16 console.log("寫完做業"); 17 resolve("做業本"); 18 },1000); 19 }); 20 return p; 21 } 22 23 rebuy().then(rework).catch(function(data){ 24 console.log(data); 25 });
2. 它的另外一個做用是,當執行 resolve 的回調(也就是上面 then 中的第一個參數)時,若是拋出異常了(代碼出錯了),那麼也不會報錯卡死 js,而是會進到這個 catch 方法中。
1 function buy(){ 2 console.log("開始買筆"); 3 var p = new Promise(function(resolve,reject){ 4 setTimeout(function(){ 5 console.log("買了筆芯"); 6 resolve("數學做業"); 7 },1000); 8 }); 9 return p; 10 } 11 12 function work(data){ 13 console.log("開始寫做業:"+data); 14 var p = new Promise(function(resolve,reject){ 15 setTimeout(function(){ 16 console.log("寫完做業"); 17 resolve("做業本"); 18 },1000); 19 }); 20 return p; 21 } 22 23 buy().then(function(data){ 24 throw new Error("買了壞的筆芯"); 25 work(data); 26 }).catch(function(data){ 27 console.log(data); 28 });
Promise 的 all 方法提供了並行執行異步操做的能力,而且在全部異步操做執行完後才執行回調。
好比下面代碼,兩個個異步操做是並行執行的,等到它們都執行完後纔會進到 then 裏面。同時 all 會把全部異步操做的結果放進一個數組中傳給 then。
1 //買做業本 2 function cutUp(){ 3 console.log('挑做業本'); 4 var p = new Promise(function(resolve, reject){ //作一些異步操做 5 setTimeout(function(){ 6 console.log('挑好購買做業本'); 7 resolve('新的做業本'); 8 }, 1000); 9 }); 10 return p; 11 } 12 13 //買筆 14 function boil(){ 15 console.log('挑筆芯'); 16 var p = new Promise(function(resolve, reject){ //作一些異步操做 17 setTimeout(function(){ 18 console.log('挑好購買筆芯'); 19 resolve('新的筆芯'); 20 }, 1000); 21 }); 22 return p; 23 } 24 25 Promise.all([cutUp(),boil()]).then(function(results){ 26 console.log("寫做業的工具都買好了"); 27 console.log(results); 28 });
race 按字面解釋,就是賽跑的意思。race 的用法與 all 同樣,只不過 all 是等全部異步操做都執行完畢後才執行 then 回調。而 race 的話只要有一個異步操做執行完畢,就馬上執行 then 回調。
注意:其它沒有執行完畢的異步操做仍然會繼續執行,而不是中止。
這裏咱們將上面樣例的 all 改爲 race
1 Promise.race([cutUp(), boil()]).then(function(results){ 2 console.log("哈哈,我先買好啦");
3 console.log(results); 4 });
race 使用場景不少。好比咱們能夠用 race 給某個異步請求設置超時時間,而且在超時後執行相應的操做。
請求某個圖片資源
1 function requestImg(){ 2 var p = new Promise(function(resolve, reject){ 3 var img = new Image(); 4 img.onload = function(){ 5 resolve(img); 6 } 7 img.src = 'xxxxxx'; 8 }); 9 return p; 10 } 11 12 //延時函數,用於給請求計時 13 function timeout(){ 14 var p = new Promise(function(resolve, reject){ 15 setTimeout(function(){ 16 reject('圖片請求超時'); 17 }, 5000); 18 }); 19 return p; 20 } 21 22 Promise.race([requestImg(), timeout()]).then(function(results){ 23 console.log(results); 24 }).catch(function(reason){ 25 console.log(reason); 26 }); 27 //上面代碼 requestImg 函數異步請求一張圖片,timeout 函數是一個延時 5 秒的異步操做。咱們將它們一塊兒放在 race 中賽跑。 28 //若是 5 秒內圖片請求成功那麼便進入 then 方法,執行正常的流程。 29 //若是 5 秒鐘圖片還未成功返回,那麼則進入 catch,報「圖片請求超時」的信息。
博客地址:https://ainyi.com/16