一步一步實現一個Promise A+規範的 Promise

2015年6月,ES2015(即ES6)正式發佈後受到了很是多的關注。其中很重要的一點是 Promise 被列爲了正式規範。
在此以前不少庫都對異步編程/回調地獄實現了類 Promise 的應對方案,好比 bluebird、Angular 的 Q 和大名鼎鼎的 jQuery 的 deffered 等。

爲了便於理解,本文將分爲三個部分,每一個部分實現 Promise 的一部分特性,最終一步一步的實現一個完整的、遵循 promise A+ 規範的 Promise。

Promise A+ 規範規定,Promise 有三種狀態,分別是 pending(默認狀態,表明等待)、 fulfilled(表明成功)、 rejected(表明失敗)。

來看看 Promise 的用法,以及它有哪些特性。

var fn = new Promise(function (resolve, reject) {    
    // 異步操做    
    setTimeout(function() {        
        resolve('resolve 01')        
        // 因爲reslove和 reject 是互斥的,由於已經調用了 resolve,這裏reject不會執行 
        reject('reject 01')
    }, 500)
})
fn().then(function (data) {
    console.log('成功1:', data)
    return new Promise(function (resolve, reject) {
        reject('reject 02')
    }
)}, function (err) {
    console.log('失敗1:', err)
})
.then(function (data) {
    console.log('成功2:', data)
}, function (err) {
    console.log('失敗2:', err)
}).then(function (data) {
    console.log('成功2:', data)}, function (err) {
    console.log('失敗2:', err)
})複製代碼

結果:前端


能夠看到,Promise 一般會有這些特性:ajax

①、Promise 類有 then 方法,then 方法有兩個參數,分別是 Promise 成功和失敗的回調,且兩者互斥,調用了其中一個,另一個就不會執行

②、Promise 支持鏈式調用,then 的返回值能夠是一個 Promise,也能夠是一個普通值,若是是 一個普通的值,那麼就會當作下一個 then 成功的回調函數的參數

③、Promis 還有其它擴展方法

說了一堆有的沒的,下面就開始來實現這個東西吧~

=================================================

一,Promise 類的實現

=> Promise 有 then 方法:

var Promise = function (executor) {
    console.log('證實不是用的原生 Promise')
    var _this = this
    this.status = 'pending'
     // 默認狀態,能夠轉化爲 resolved 和 rejected
    this.successVal = undefined
    this.failVal = undefined

    // 執行了成功或失敗後,要將狀態對應修改爲成功和失敗
    function resolve (val) {
        if ( _this.status === 'pending' ) {
            _this.status = 'resolved'
            _this.successVal = val
        }
    }
    function reject (val) {
        if ( _this.status === 'pending' ) {
            _this.status = 'rejected'
            _this.failVal = val
        }
    }

    try {
        // 應該還記得,Promise 的參數是一個函數吧,咱們稱之爲 executor(執行器)
        // 同時這個函數接收2個參數,分別是成功和失敗的回調函數
        executor(resolve, reject)
    } catch (e) {
        // 若是發生異常,直接reject捕獲
        reject(e)
    }
}
// then 方法接收2個參數,分別是成功和失敗的回調函數
Promise.prototype.then = function (onFulfilled, onRejected) {
    var _this = this    
    // 顯然要根據當前狀態來執行成功或失敗的回調了
    if ( _this.status === 'resolved' ) {
        onFulfilled(_this.successVal)
    }
    if ( _this.status === 'rejected' ) {
        onFulfilled(_this.failVal)
    }
}複製代碼
來試試效果:

var fn = new Promise(function (resolve, reject) {
    resolve('oooook~')
}) 
fn.then(function (data) {
    console.log('success: ', data)}, function (err) {
    console.log('err: ', err)
})複製代碼

結果:編程


結果看上去很美。但若是改爲下面這樣呢?promise

var fn = new Promise(function (resolve, reject) {
    setTimeout(function () {
        resolve('oooook~')
    }, 500)
}) 
fn.then(function (data) {
    console.log('success: ', data)
}, function (err) {
    console.log('err: ', err)
})複製代碼
結果:


ok,問題來了,Promise 費大力氣搞出來可不是隻能作同步調用的,畢竟有了 ajax 以後前端纔會發展到今天這麼繁榮,因此須要讓咱們的 Promise 支持異步。

修改以下(註釋內是對新增代碼的說明):

var Promise = function (executor) {
    console.log('證實不是用的原生 Promise 的一句廢話')
    var _this = this    this.status = 'pending'
     // 默認狀態,能夠轉化爲 resolved 和 rejected
    this.successVal = undefined
    this.failVal = undefined
    // ----------------- 表示新增代碼 --------------------    // 用於存放成功和失敗的回調
    this.onFulfilledList = []
    this.onRejectedList = []

    function resolve (val) {
        if ( _this.status === 'pending' ) {
            _this.status = 'resolved'
            _this.successVal = val
            // -------------- 執行全部的成功回調 ---------------
            _this.onFulfilledList.forEach(function(fn){
                fn()
            })
        }
    }
    function reject (val) {
        if ( _this.status === 'pending' ) {
            _this.status = 'rejected'
            _this.failVal = val
            // -------------- 執行全部的失敗回調 ---------------
            _this.onRejectedList.forEach(function(fn){
                fn()
            })
        }
    }
    try {
        executor(resolve, reject)
    } catch (e) {
        reject(e)
    }
}
Promise.prototype.then = function (onFulfilled, onRejected) {

    var _this = this

    if ( _this.status === 'resolved' ) {
        onFulfilled(_this.successVal)
    }
    if ( _this.status === 'rejected' ) {
        onFulfilled(_this.failVal)
    }

    // ----------------- 對異步調用的處理 -------------------
    // 說明:若是是異步調用,走到 then 方法裏的時候,status 尚未被修改,仍然
    // 是 pending 狀態,因此這時候須要再回去執行 executor 裏的對應方法,而對應的
    // 方法會將對應的存放回調的 list 裏的方法執行(相似發佈-訂閱模式同樣的處理)
    if ( _this.status === 'pending' ) {
        _this.onFulfilledList.push(function () {
            onFulfilled(_this.successVal)
        })
        _this.onRejectedList.push(function () {
            onRejected(_this.failVal)
        })
    }
}複製代碼
看看效果:


目前來看已經支持異步了~bash


據說大家都在黑我大星爵?星爵那麼萌,大家忍心嗎異步



下一篇:一步一步來實現一個Promise A+規範的 Promise之二:Promise 鏈式調用異步編程

相關文章
相關標籤/搜索