1. 程式人生 > 實用技巧 >Spring Cloud Ribbon服務列表的快取與更新(二)

Spring Cloud Ribbon服務列表的快取與更新(二)

Promise作為ES6中最重要的特性之一,我們有必要掌握。那Promise到底是什麼呢,讓我們打印出來看一下:

可以看出,Promise是一個建構函式,自己身上有all、race、reject、resolve等方法,原型上有then、catch等方法。讓我們來建立一個Promise物件:

var p = new Promise(function(resolve, reject) {
    // 非同步處理
    // 處理結束後,呼叫resolve 或 reject
})

Promise的建構函式接收一個引數,是一個回撥函式(在回撥中執行一些操作,例如非同步),並且給這個函式傳入兩個引數:resolve(解析),reject(拒絕),分別表示非同步操作執行成功後的回撥函式和非同步操作執行失敗後的回撥函式。其實這裡用“成功”和“失敗”來描述並不準確,按照標準來講,resolve是將Promise的狀態置為fullfiled,reject是將Promise的狀態置為rejected。

var p = new Promise(function(resolve, reject){
    //當非同步程式碼執行成功時,我們才會呼叫resolve(...), 當非同步程式碼失敗時就會呼叫reject(...)
    //在本例中,我們使用setTimeout(...)來模擬非同步程式碼,實際編碼時可能是XHR請求或是HTML5的一些API方法
    setTimeout(function(){
        resolve("成功!")
    }, 1000)
})
 
p.then(function(data){
    //data的值是上面呼叫resolve(...)方法傳入的值
//data引數不一定非要是字串型別,這裡只是舉個例子 console.log(data) })

Promise 物件有以下兩個特點:

1、物件的狀態不受外界影響。Promise 物件代表一個非同步操作,有三種狀態:

  • pending: 初始狀態,不是成功或失敗狀態。
  • fulfilled: 意味著操作成功完成。
  • rejected: 意味著操作失敗。

只有非同步操作的結果,可以決定當前是哪一種狀態,任何其他操作都無法改變這個狀態。這也是 Promise 這個名字的由來,它的英語意思就是「承諾」,表示其他手段無法改變。

2、一旦狀態改變,就不會再變,任何時候都可以得到這個結果。Promise 物件的狀態改變,只有兩種可能:從 Pending 變為 Resolved 和從 Pending 變為 Rejected。只要這兩種情況發生,狀態就凝固了,不會再變了,會一直保持這個結果。就算改變已經發生了,你再對 Promise 物件添加回調函式,也會立即得到這個結果。這與事件(Event)完全不同,事件的特點是,如果你錯過了它,再去監聽,是得不到結果的。

Promise 優缺點:

有了 Promise 物件,就可以將非同步操作以同步操作的流程表達出來,避免了層層巢狀的回撥函式。此外,Promise 物件提供統一的介面,使得控制非同步操作更加容易。

Promise 也有一些缺點。首先,無法取消 Promise,一旦新建它就會立即執行,無法中途取消,所以我們用Promise的時候一般是包在一個函式中,在需要的時候去執行這個函式。其次,如果不設定回撥函式,Promise 內部丟擲的錯誤,不會反應到外部。第三,當處於 Pending 狀態時,無法得知目前進展到哪一個階段(剛剛開始還是即將完成)。

//將Promise物件包在函式中
function getP(msg){
    var p = new Promise(function(resolve, reject){
        //做一些非同步操作
        setTimeout(function(){
            console.log(msg)
            resolve('成功')
        }, 1000)
    })
    return p            
}
getP('Promise')

Promise.prototype.then方法:鏈式操作

從表面上看,Promise只是解決了回撥地獄的問題,而實質上,Promise的精髓是“狀態”,用維護狀態、傳遞狀態的方式來使得回撥函式能夠及時呼叫。

Promise.prototype.then 方法返回的是一個新的 Promise 物件,因此可以採用鏈式寫法。

getP('Promise1').then(function(data){
    console.log(data)
    return getP('Promise2')
}).then(function(data) {
    console.log(data)
    return getP('Promise3')
}).then(function(data){
     console.log(data)
})

這樣能夠按順序,每隔1秒輸出每個非同步回撥中的內容,執行結果如下:

在then方法中,你也可以直接return資料而不是Promise物件,在後面的then中就可以接收到資料了,比如我們把上面的程式碼修改成這樣:

getP('Promise1').then(function(data){
    console.log(data)
    return getP('Promise2')
}).then(function(data) {
    console.log(data)
    return '資料'
}).then(function(data){
     console.log(data)
})

結果如下:

Promise.prototype.catch方法:捕捉錯誤

Promise.prototype.catch 方法是 Promise.prototype.then(null, rejection) 的別名,它和then的第二個引數一樣,用於指定發生錯誤時的回撥函式。

getP("Promise").then(function(data) {
console.log(data)
// some code }).catch(function(error) { // 處理前一個回撥函式執行時發生的錯誤 console.log(error) })

Promise 物件的錯誤具有"冒泡"性質,會一直向後傳遞,直到被捕獲為止。也就是說,錯誤總是會被下一個 catch 語句捕獲。

getP("Promise1").then(function(data) {
console.log(data)
return
getP('Promise2')
}).then(function(data) { 
console.log(data)
// some code
}).catch(function(error) {
// 處理前兩個回撥函式的錯誤
})

也就是說當代碼出錯時並不會報錯卡死,而是會進到catch方法裡面去,而且把錯誤原因傳到了error引數中,這與我們的try/catch語句有相同的功能。

Promise.all方法

Promise.all 方法用於將多個 Promise 例項,包裝成一個新的 Promise 例項。

var p = Promise.all([p1,p2,p3])

上面程式碼中,Promise.all 方法接受一個數組作為引數,p1、p2、p3 都是 Promise 物件的例項。(Promise.all 方法的引數不一定是陣列,但是必須具有 iterator 介面,且返回的每個成員都是 Promise 例項。)

p 的狀態由 p1、p2、p3 決定,分成兩種情況。

  • 只有p1、p2、p3的狀態都變成fulfilled,p的狀態才會變成fulfilled,此時p1、p2、p3的返回值組成一個數組,傳遞給p的回撥函式。
  • 只要p1、p2、p3之中有一個被rejected,p的狀態就變成rejected,此時第一個被reject的例項的返回值,會傳遞給p的回撥函式。
var p1 = getP('Promise1')
var p2 = getP('Promise2')
var p3 = getP('Promise3')
Promise.all([p1,p2,p3])
.then(function(results){
    console.log(results)
})

Promise.race方法

all方法的效果實際上是:誰跑的慢,就以誰為準執行回撥,那麼相對的就有另一個方法:誰跑的快,以誰為準執行回撥

Promise.race 方法同樣是將多個 Promise 例項,包裝成一個新的 Promise 例項。

var p = Promise.race([p1,p2,p3])

上面程式碼中,只要p1、p2、p3之中有一個例項率先改變狀態,p的狀態就跟著改變。那個率先改變的Promise例項的返回值,就傳遞給p的返回值。