直接打印出來看看吧,console.dir(Promise)。
這么一看就明白了,Promise是一個構造函數,自己身上有all、reject、resolve這幾個眼熟的方法,原型上有then、catch等同樣很眼熟的方法。這么說用Promise new出來的對象肯定就有then、catch方法嘍,沒錯。
Promise,簡單說就是一個容器,里面保存着某個未來才會結束的事件(通常是一個異步操作)的結果。從語法上說,Promise 是一個對象,從它可以獲取異步操作的消息。
Promise 提供統一的 API,各種異步操作都可以用同樣的方法進行處理。
Promise對象有以下兩個特點:
(1)對象的狀態不受外界影響。Promise對象代表一個異步操作,有三種狀態:Pending(進行中)、Resolved(已完成,又稱 Fulfilled)和Rejected(已失敗)。只有異步操作的結果,可以決定當前是哪一種狀態,任何其他操作都無法改變這個狀態。這也是Promise這個名字的由來,它的英語意思就是“承諾”,表示其他手段無法改變。
(2)一旦狀態改變,就不會再變,任何時候都可以得到這個結果。Promise對象的狀態改變,只有兩種可能:從Pending變為Resolved和從Pending變為Rejected。只要這兩種情況發生,狀態就凝固了,不會再變了,會一直保持這個結果。就算改變已經發生了,你再對Promise對象添加回調函數,也會立即得到這個結果。這與事件(Event)完全不同,事件的特點是,如果你錯過了它,再去監聽,是得不到結果的。
有了Promise對象,就可以將異步操作以同步操作的流程表達出來,避免了層層嵌套的回調函數。此外,Promise對象提供統一的接口,使得控制異步操作更加容易。
簡單來說,Promise 就是用同步的方式寫異步的代碼,用來解決回調問題
then()方法
then 方法就是把原來的回調寫法分離出來,在異步操作執行完后,用鏈式調用的方式執行回調函數。
而 Promise 的優勢就在於這個鏈式調用。我們可以在 then 方法中繼續寫 Promise 對象並返回,然后繼續調用 then 來進行回調操作。
可有兩個參數,第一個是成功 resolve 調用的方法,第二個是失敗 reject 調用的方法
下面做一個買筆寫作業上交的演示,它們是層層依賴的關系,下一步的的操作需要使用上一部操作的結果。(這里使用 setTimeout 模擬異步操作),正式開發可以用 ajax 異步
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));
reject()方法:
上面樣例我們通過 resolve 方法把 Promise 的狀態置為完成態(Resolved),這時 then 方法就能捕捉到變化,並執行“成功”情況的回調。
而 reject 方法就是把 Promise 的狀態置為已失敗(Rejected),這時 then 方法執行“失敗”情況的回調(then 方法的第二參數)
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 });
catch()方法:
1. 它可以和 then 的第二個參數一樣,用來指定 reject 的回調
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 });
all()方法:
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 按字面解釋,就是賽跑的意思。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,報“圖片請求超時”的信息。