一、Promise歷史javascript
Promise 是異步編程的一種解決方案,比咱們傳統的回調函數、事件更加合理,最先由社區提出並實現,ES6以爲很ok,就寫進了語言標準中,統一了語法,而且提供了原生的Promise對象。vue
所謂的Promise,其實就是一個容器,裏面保存着將來纔會結束的事件(一般是一個異步操做)的結果,從javascript的語法上講就是一個對象,從裏面能獲取異步操做的信息,Promise提供統一的api,各類異步操做均可以使用一樣的方法來進行處理。java
二、Promise的特色react
特色一:Promise對象的狀態不受外界影響。表明着一個異步操做,有三種狀態:Pending(進行中)、Fulfilled(已成功)和Reject(已失敗)。只有異步操做的結果才能夠決定當前是哪種狀態,任何其餘操做都沒法改變這個狀態,這也就是Promise的由來。「承諾」,就是其餘手段沒法更改。ios
特色二:一旦狀態改變了,就不會再改變。任什麼時候候均可以獲得這個結果。Promise對象的狀態的改變只會是兩種可能:從Pending變成Fulfilled和Pending變爲Rejected。這要這兩種狀況發生,狀態就凝固了,不會再改變了,而是一直保持這樣一個結果這時候就成Resolved(已定型)。就算改變已經發生,再對Promise對象添加回調函數,也會當即獲得這個結果。這與事件(event)徹底不一樣,事件的特色咱們都知道,若是錯過它,就監聽不到結果的。編程
ES6規定,Promise對象是一個構造給函數,是用來生成Promise實例的。json
let Promise=new Promise(function(resolve,reject){
/*邏輯代碼*/
if(/*異步操做成功*/){
resolve(value)
}else{
reject(error)
}
})
複製代碼
Promise構造函數式會接受兩個參數的,該函數的兩個參數是resolve和reject,是javascript引擎提供的,不用本身部署。axios
resolve函數的做用是,將Promise對象的狀態從「未完成」到「成功」(就是從Pending變爲Resolved),在異步操做成功的時候,並將異步操做的結果做爲參數傳遞進去;reject函數的做用是將Promise對象的狀態從「未完成」變成爲「失敗」(即從Pending變爲Rejected),在異步操做失敗的時候調用,將異步錯誤傳遞出去。api
Promise實例生成之後,能夠用then方法分別指定Resolve狀態和Reject狀態的回調函數。promise
let Promise=new Promise(function(resolve,reject){})
Promise.then(function(value){
//success
},function(error){
//failuer
})
複製代碼
then方法能夠接受兩個回調函數做爲參數,第一個是Promise對象變成Resolved時調用,第二個是Promise對象的狀態變爲Rejectd的時候調用,第二個函數式可選的,也能夠不寫,這兩個函數都接受Promise對象傳出值做爲參數。舉一個栗子吧:
function timeout(ms){
return new Promise((resolve,reject)=>{
setTimeout(resolve,ms,'done')
})
}
timeout(100).then((value)=>{
console.log(value)
})
複製代碼
上面的代碼,timeout方法會返回一個Promise的實例,一段時間之後纔會發生的結果,過了指定時間(ms)之後,Promise實例就會變成Resolve,就會觸發then的方法綁定的回調函數。
Promise初始化實例與之後就會當即執行,不妨看一下下面的打印驗證:
let promise=new Promise(function(resolve,reject){
console.log("我是Promise")
resolve()
})
promise.then(function(){
comsole.log("我時候then的回調")
})
console.log('hello,promise!')
//我是Promise
//hello,promise!
//我時候then的回調
複製代碼
上面的代碼中,Promise實例化以後會當即執行,因此首先輸出的是Promise,而後,then方法指定的回調函數將在當前腳本全部同步腳本執行完纔會執行,因此上述代碼中「我時候then的回調」最後輸出。
舉一個頁面圖片異步加載的栗子:
function loadImageAsync(url){
retutn new Promise(function(resolve,reject){
var image=new Image();
image.onload=function(){
resolve(image)
}
image.onerror=function(){
reject(new Error('could not load image at'+url))
}
image.src=url
})
}
複製代碼
上面舉出了經常使用防止頁面阻塞異步加載圖片的栗子,若是加載成功,就調用resolve方法,不然就調用reject方法。
若是不過癮的話,就簡單來實現一下當前單頁面比較火的Vue、React框架,開發所用的Http請求axios和fetch的基於Promise實現Ajax的過程:
let getJSON=function(url){
let promise=new Promise(function(){
let client=new XMLHttpRequest()
client.open('GET',url)
client.onreadystatechange=hander
cilent.responseType='json'
client.setRequestHeader('Accept',"application/json")
client.send()
function hander(){
if(this.readystate!=4){
return
}
if(this.statues===200){
resolve(this.response)
}else{
reject(new Error(this.stautsText))
}
}
})
return promise
}
//使用
getJSON('/xxx/xx.json').then((json)=>{
console.log('contents'+json)
},(error)=>{
console.log("請求出錯"+error)
})
複製代碼
上面的代碼中,getJSON是對XMLHttpRequest對象基於promise的封裝,能夠發出一個針對json數據的HTTP請求,並返回一個promise對象,能夠看見getJSON內部resolve和reject函數都帶有參數,那麼這些參數會被傳遞給回調函數,reject函數的參數一般是Error對象的實例,表示拋出錯誤,resolve函數的參數除了正常的值外,還能夠是另一個promise實例,比方說:
let pro1=new Promise(function(resolve,reject){
//....
})
let pro2=new Promise(function(resolve,reject){
//...
resolve(pro1)
})
複製代碼
上面的代碼中,pro1和pro2都是promise的實例,可是pro2的resolve方法將pro1做爲參數,即一個異步操做的結果返回另外一個異步操做結果。此時pro1的狀態就會傳遞給pro2,也就是說pro1決定了pro2的狀態,若是pro1狀態是pending,那麼pro2的回調函數就會等待pro1的狀態已經resolve或者rejected,那麼pro2的回調函數才當即執行。
Promise實例具備then方法,即then方法是定義在原型對象Promise.prototype上的,做用是給Promise實例添加狀態改變時的回調函數。then方法的第一個參數是Resolve狀態的回調函數,第二個是Rejected(可選)狀態的回調函數。
then方法返回的是一個新的Promise實例,不是原來那個Promise實例,所以能夠採用鏈式寫法,即then方法後面再調用另外一個then方法。
getJSON('xxx/xx.json').then(function(json){
return json.post
}).tnen(function(post){
})
複製代碼
採用鏈式的then能夠指定一組按照次序調用的回調函數,這時,前一個回調函數有可能返回的仍是一個異步操做,即Promise實例,然後一個回調函數會等待該Promise對象狀態改變才調用。
Promise.prototype.catch的方法是.then(null,rejection)的別名,用於指定發生錯誤的回調函數。
舉個栗子:
getJSON('xxx/xx.json').then(function(json){
//...
}).catch(function(err){
console.log('錯誤'+error)
})
複製代碼
上面的代碼,只要then中某一步異步操做拋出錯誤,狀態變成Rejected,都會被catch()所捕獲到,而後指定其回調函數執行,處理這個錯誤。Promise對象的錯誤具備冒泡機制,會一直向後傳遞,一直到被catch()處理函數所捕獲,也就是說錯誤會被下一個catch語句捕獲。
getJSON('xxx/xx.json').then(function(json){
//...some code1
}).then(function(json){
//...some code2
}).catch(function(err){
console.log('錯誤'+error)
})
複製代碼
上面的代碼中一共有四個Promise對象,getJSON產生,兩個then產生兩個,其中任何一個報錯,都會被最後一個catch所捕獲,通常使用的時候,注意不要在then()中定義Rejected回調函數,官方建議老是使用catch方法處理錯誤異常回調。
.catch()和傳統的try/catch不一樣是,若是沒有使用catch方法指定錯誤的的回調函數,Peomise對象拋出的錯是不會傳遞到外層代碼的,也就是說try/catch是沒有反應的。
var errorNotCatch=function(){
return new Promise((resolve,reject)=>{
fs.readFile('././xxx',(err)=>{
resolve(data)
})
})
}
errorNotCatch().then(data=>{
console.log('good');
})
複製代碼
上面的代碼。是不會輸出good的try/catch 是捕捉不到錯誤的,可是瀏覽器會打出「data is not defined」
小夥伴們,以上的基礎介紹,但願對大家在vue、react開發中的Promise實現的一些東西有一個更好的認識。