做爲一個入門級前端,今天是一個很是值得記念的日子,由於這是我第一次在論壇上發表帖子,做爲起步。雖然我以爲本身水平仍是十分的有限,對一些細節的理解還不是很透徹,可是仍是要邁出這一步,不論是給別的新手做爲學習參考,仍是本身之後回顧,總以爲須要把本身的成長記錄下來,但願本身之後仍是要多堅持,若是有不對的地方仍是但願你們及時提出來,共同進步前端
今天有時間翻到了es6的promise,可能你們都對此熟悉不過,我以前一直以爲promise也很簡單,可是今天確實讓我對promise有了一個新的瞭解,之前的理解多是錯誤的。。。先來看看官方的promise的定義是:es6
所謂Promise,簡單說就是一個容器,裏面保存着某個將來纔會結束的事件(一般是一個異步操做)的結果。從語法上說,Promise 是一個對象,從它能夠獲取異步操做的消息。Promise 提供統一的 API,各類異步操做均可以用一樣的方法進行處理。數組
特色:promise
(1)對象的狀態不受外界影響。Promise對象表明一個異步操做,有三種狀態:pending(進行中)、fulfilled(已成功)和rejected(已失敗)。只有異步操做的結果,能夠決定當前是哪種狀態,任何其餘操做都沒法改變這個狀態。這也是Promise這個名字的由來,它的英語意思就是「承諾」,表示其餘手段沒法改變bash
(2)一旦狀態改變,就不會再變,任什麼時候候均可以獲得這個結果。Promise對象的狀態改變,只有兩種可能:從pending變爲fulfilled和從pending變爲rejected。只要這兩種狀況發生,狀態就凝固了,不會再變了,會一直保持這個結果,這時就稱爲 resolved(已定型)。若是改變已經發生了,你再對Promise對象添加回調函數,也會當即獲得這個結果。這與事件(Event)徹底不一樣,事件的特色是,若是你錯過了它,再去監聽,是得不到結果的。異步
初讀上面這段話我讀了不下5次,可是我仍是沒能真正瞭解其真正表達的意思,因而我查閱了部分資料,終於找到了一個比較容易理解的說法,這個估計小白應該是能夠看得懂得。編輯器
就拿作飯吃飯洗碗來舉例子吧,這是三個步驟,第一步作飯,再第二步吃飯的時候咱們須要拿到第一步作的飯,在第三步洗碗的時候咱們須要拿到第二步的碗筷,並且這三個步驟必須是按照順序執行,有嚴格的前後順序。函數
//作飯
function cook(){
console.log('開始作飯。');
var p = new Promise(function(resolve, reject){ //作一些異步操做
setTimeout(function(){
console.log('作飯完畢!');
resolve('雞蛋炒飯');
}, 1000);
});
return p;
}
//吃飯
function eat(data){
console.log('開始吃飯:' + data);
var p = new Promise(function(resolve, reject){ //作一些異步操做
setTimeout(function(){
console.log('吃飯完畢!');
resolve('一個碗和一雙筷子');
}, 2000);
});
return p;
}
//洗碗
function wash(data){
console.log('開始洗碗:' + data);
var p = new Promise(function(resolve, reject){ //作一些異步操做
setTimeout(function(){
console.log('洗碗完畢!');
resolve('乾淨的碗筷');
}, 2000);
});
return p;
}
//函數調用
cook().then(res1 => {
console.log(res1,'這是第一步傳遞給第二步的參數')
return eat(res1)
}).then(res2 => {
console.log(res2,'這是第二步傳給第三步的參數')
return wash(res2)
}).then(res3 => {
console.log(res3,'飯吃完了還你乾淨的碗筷')
})
複製代碼
結果以下: 學習
看完上面的代碼你們可能會有好多疑問,我在這裏把我當時初學promise的疑問和你們分享一下:ui
答:Promise也有一些缺點。首先,沒法取消Promise,一旦新建它就會當即執行,沒法中途取消。其次,若是不設置回調函數,Promise內部拋出的錯誤,不會反應到外部。第三,當處於pending狀態時,沒法得知目前進展到哪個階段(剛剛開始仍是即將完成)。
這是promise的一些缺點,一旦新建它就會當即執行 因此爲了控制這個promise對象何時執行,在開發過程當中咱們須要在外面包裹一個函數,經過調用函數的形式來控制promise的執行。你們能夠在本身的編輯器中試試。
new Promise(function(resolve, reject) {
setTimeout(()=> {
console.log('開車!!!')
},2000)
});
複製代碼
答:由於有了Promise對象,就能夠將異步操做以同步操做的流程表達出來,避免了層層嵌套的回調函數。此外,Promise對象提供統一的接口,使得控制異步操做更加容易。
//函數調用
cook().then(res1 => {
console.log(res1,'這是第一步傳遞給第二步的參數')
return eat(res1)
}).then(res2 => {
console.log(res2,'這是第二步傳給第三步的參數')
return wash(res2)
}).then(res3 => {
console.log(res3,'飯吃完了還你乾淨的碗筷')
})
複製代碼
你們看個人這段代碼 在執行了cook()函數的時候cook函數return出來一個promise對象,promise對象上有.then()或.catch()方法,那麼直接cook().then就能夠在.then()方法中咱們能夠拿到promise對象中向外傳遞的參數,這個參數咱們將傳遞給下一個eat()函數,做爲eat()函數的參數繼續執行。 而eat()函數也return了一個promise對象。那麼咱們將咱們的這段代碼拆解一下:
第一步: cook() //拿到的是cook return出來的promise對象
第二步: cook().then(res => {
console.log(res) //拿到內部向外部傳遞的參數
})
第三步: cook().then(res => {
console.log(res)
return eat(res) //eat執行之後return的是eat的promise對象,而後再把這個對象繼續向外return
}) //那麼第三步的最終結果就是eat()的promise對象
第四步: cook().then(res => {
console.log(res)
return eat(res) //此時的結果是eat()的promise對象
}).then(res2 => {
// 此時eat的promise又有.then方法 .....以此類推
})
複製代碼
咱們就這樣一步一步的完成了整個作飯、吃飯、洗碗的整個流程。 縱觀以上代碼你會發現雖然每個操做流程中我都是以異步函數setTimeout來進行的,可是在調用過程當中確是按照 作飯-吃飯-洗碗的正常流程表達的。這不是promise的有了Promise對象,就能夠將異步操做以同步操做的流程表達出來,避免了層層嵌套的回調函數
//作飯
function cook(){
console.log('開始作飯。');
var p = new Promise(function(resolve, reject){ //作一些異步操做
setTimeout(function(){
console.log('飯糊了....無法吃');
reject('糊了的飯');
}, 1000);
});
return p;
}
//吃飯
function eat(data){
console.log('開始吃飯:' + data);
var p = new Promise(function(resolve, reject){ //作一些異步操做
setTimeout(function(){
console.log('吃飯完畢!');
resolve('一個碗和一雙筷子');
}, 2000);
});
return p;
}
cook().then(res1 => {
console.log(res1,'這是第一步傳遞給第二步的參數')
return eat(res1)
}).catch(err => {
console.log(err,'返回錯誤')
})
複製代碼
catch()方法用來指定 reject 的回調。
function tackBus1(){
var p = new Promise(function(resolve, reject){
//作一些異步操做
setTimeout(function(){
console.log('甲正在上車');
resolve('甲坐好了');
}, 1000);
});
return p;
}
function tackBus2(){
var p = new Promise(function(resolve, reject){
//作一些異步操做
setTimeout(function(){
console.log('乙正在上車');
resolve('乙坐好了');
}, 2000);
});
return p;
}
function tackBus3(){
var p = new Promise(function(resolve, reject){
//作一些異步操做
setTimeout(function(){
console.log('丙正在上車');
resolve('丙坐好了');
}, 3000);
});
return p;
}
Promise.all([tackBus1(),tackBus2(),tackBus3()]).then(res => {
console.log(res)
})
複製代碼
司機在等人上車,在乘客都坐穩了之後開車發車,這就用Promise.all來執行,all接收一個數組參數,裏面的值最終都算返回Promise對象。這樣,三個異步操做的並行執行的,等到它們都執行完後纔會進到then裏面。那麼,三個異步操做返回的數據哪裏去了呢?都在then裏面呢,all會把全部異步操做的結果放進一個數組中傳給then,就是上面的results。因此上面代碼的輸出結果就是:
這是.all的方法,promise還有一種.race的方法,它和all方法的區別就是: all方法的效果其實是誰跑的慢,以誰爲準執行回調,那麼相對的就有另外一個方法誰跑的快,以誰爲準執行回調,剛剛的執行結果我設置了他們的時間間隔分別是1s,2s,3s,在等最慢的執行完之後才執行了all這個回調方法,如今我們來試試promise.race方法
function tackBus1(){
var p = new Promise(function(resolve, reject){
//作一些異步操做
setTimeout(function(){
console.log('甲正在上車');
resolve('甲坐好了');
}, 1000);
});
return p;
}
function tackBus2(){
var p = new Promise(function(resolve, reject){
//作一些異步操做
setTimeout(function(){
console.log('乙正在上車');
resolve('乙坐好了');
}, 2000);
});
return p;
}
function tackBus3(){
var p = new Promise(function(resolve, reject){
//作一些異步操做
setTimeout(function(){
console.log('丙正在上車');
resolve('丙坐好了');
}, 3000);
});
return p;
}
Promise.race([tackBus1(),tackBus2(),tackBus3()]).then(res => {
console.log(res)
})
複製代碼
結果以下:
能夠看出來在甲執行完畢後當即就執行了.race()方法,可是不耽誤其餘兩個異步操做的進行,.race()中拿到的參數也只是當前最早執行完的異步操做中傳遞出來的參數。在看到promise的時候有一個地方仍是令我有困惑,如今先留一個懸念,你們能夠先看看下面這段代碼,你以爲輸出結果是什麼呢? 咱們下回見!
setTimeout(function(){
console.log('1')
});
new Promise(function(resolve){
console.log('2');
resolve();
}).then(function(){
console.log('3')
});
console.log('4');
複製代碼