以最輕量的方式解釋Promise

接觸過JS的開發人員應該都有用過Promise處理異步編程,它在語法上非常直觀的用“then”去對當下所要做得執行和在期之後所要執行的代碼做了封裝。

function test() {
    let promise = new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('after one second');
        }, 1000);
    })

    return promise;
}

test().then(res => {
    console.log(res); 
})

console.log("before one second");

// "before one second"
// "after one second"

對沒有接觸過異步編程的工程師來講,這看起來似乎有魔術般的效應,彷彿是將時間做了停頓。不過花點心思去想Promise的內部機制,大家就會發現,它的核心在於巧妙地應用高階函數的理念將異步操作進行了封裝。如果用最輕量的方法趨勢線上圖的Promise功能,其實幾行代碼便可以解決。

function Promice(fn) {
    var self = this
    self.status = 'pending' // Promise當前的狀態
    self.data = undefined // Promise的值
    self.onResolvedCallback = undefined //回調函數

    function resolve(value) {
        if (self.status === 'pending') {
            self.status = 'resolved'
            self.data = value
            if(self.onResolvedCallback) {
                self.onResolvedCallback(value)
            }
        }
    }

    try { 
        fn(resolve) 
    } catch (e) {
        reject(e)
    }
}

在這裏我將reject抓錯機制去除,針對去看Promise是如何"控制"異步操作的。上圖需要注意的是兩個重點:1. status狀態去跟蹤異步操作是否完成。2. resolve函數要做的就是更新狀態並且調用用戶指定的fn回調函數。有人可能會問:“如果只是單純的更新狀態,如何保證異步操作已經完成呢?”。這裏要注意的是,從Promise得操作說明上,resolve永遠只會在異步回調函數裏出現,所以當resolve啓動時,必然表示異步操作已完成。

有人會注意到onResolvedCallback賦值並沒有在代碼裏實現。這表示在resolve被調用之前,還會有其他的操作執行,也就是"then"函數。

Promice.prototype.then = function (onResolved) {
    var self = this
    var promise2

    if (self.status === 'resolved') {
        return promise2 = new Promice(function (resolve, reject) {
            var x = onResolved(self.data)
            if (x instanceof Promice) { 
                x.then(resolve, reject)
            }
            resolve(x) 
        })
    }

    if (self.status === 'pending') {
        return promise2 = new Promice(function (resolve, reject) {
            self.onResolvedCallback = function (value) {
                var x = onResolved(self.data)
                if (x instanceof Promice) {
                    x.then(resolve, reject)
                }
            }
        })
    }
}

then函數其實在一開始就被調用了,它的責任是去查看status。如果status表明一步操作已經完成,但就直接調用fn。如果還在待定(pending),則將函數賦值給onResolveCallback。當resolve觸發時必會做處理。

總結:Promise會給很多人帶來一種時間被控制的假象,但底層核心邏輯並不複雜。這裏需要注意的是then函數很容易被視爲一個異步操作結束後調用的函數,但這明顯是個錯誤的想法。then通常在異步操作之前已經調用,他的任務往往是將傳入的回調函數放到resolve可以獲取到的作用域。雖然Promise產生的假象能讓我們更直觀的去開發,但也需要了解的是它的底層實現。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章