這樣你都不懂Promise,算我輸!

1、Promise的身世

一、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)徹底不一樣,事件的特色咱們都知道,若是錯過它,就監聽不到結果的。編程

2、基本用法

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的回調函數才當即執行。

3、Promise.prototype.then()

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對象狀態改變才調用。

4、Promise.prototype.catch()

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」

5、

小夥伴們,以上的基礎介紹,但願對大家在vue、react開發中的Promise實現的一些東西有一個更好的認識。

相關文章
相關標籤/搜索