手寫一個Promise【前端每日一題-24】

Promise的原理以及手寫一個Promise

觀察原生promise用法,咱們能夠發現,在new Promise時候傳入了一個函數,這個函數在規範中的叫法是exector 執行器
看到這裏,咱們先有一個大概思路,構建一個本身的Promise構造函數。
javascript

// 這裏咱們建立了一個構造函數 參數就是執行器
function Promise(exector) {
    
}複製代碼

好的,第一步完成, 重點來了,這個Promise內部到底幹了什麼呢 能夠看到,原生的exector中傳入了兩個參數,第一個參數執行會讓promise狀態變爲resolve, 也就是成功, 第二個執行會讓函數變爲reject狀態,也就是失敗java

而且這連個形參執行以後均可以傳入參數,咱們繼續完善代碼 咱們將這兩個形參的函數封裝在構造函數內部數組

// 這裏咱們建立了一個構造函數 參數就是執行器
function Promise(exector) {
    // 這裏咱們將value 成功時候的值 reason失敗時候的值放入屬性中
    let self = this;
    this.value = undefined;
    this.reason = undefined;
    
    // 成功執行
    function resolve(value) {
      self.value = value;
    }
    
    // 失敗執行
    function reject(reason) {
        self.reason = reason;
    }
    
    exector(resolve, reject);
}複製代碼

這裏問題來了,咱們知道,promise的執行過程是不可逆的,resolve和rejeact之間也不能相互轉化, 這裏,咱們就須要加入一個狀態,判斷當前是否在pending過程,另外咱們的執行器可能直接報錯,這裏咱們也須要處理一下。

// 這裏咱們建立了一個構造函數 參數就是執行器
function Promise(exector) {
    // 這裏咱們將value 成功時候的值 reason失敗時候的值放入屬性中
    let self = this;
    // 這裏咱們加入一個狀態標識
    this.status = 'pending';
    this.value = undefined;
    this.reason = undefined;
    
    // 成功執行
    function resolve(value) {
        // 判斷是否處於pending狀態
        if (self.status === 'pending') {
            self.value = value;
            // 這裏咱們執行以後須要更改狀態
            self.status = 'resolved';
        }
    }
    
    // 失敗執行
    function reject(reason) {
        // 判斷是否處於pending狀態
        if (self.status === 'pending') {
            self.reason = reason;
            // 這裏咱們執行以後須要更改狀態
            self.status = 'rejected';
        }
    }
    
    // 這裏對異常進行處理
     try {
        exector(resolve, reject);
    } catch(e) {
        reject(e)
    }
}複製代碼

new Promise以後咱們怎麼去改變promise對象的狀態呢, 經過原生的用法咱們瞭解到,須要使用then方法, then方法分別指定了resolved狀態和rejeacted狀態的回調函數
那怎麼知道使用哪一個回調函數呢,咱們剛不是在構造函數內部定義了status麼,這裏就用上啦,上代碼

// 咱們將then方法添加到構造函數的原型上 參數分別爲成功和失敗的回調

Promise.prototype.then = function(onFulfilled, onRejected) {
    // 獲取下this
    let self = this;
    if (this.status === 'resolved') {
        onFulfilled(self.value);
    }
    
    if (this.status === 'rejected') {
        onRejected(self.reason);
    }
}複製代碼

ok,咱們如今能夠本身運行試試

let promise = new Promise((resolve, reject) => {
     resolve("haha");
})


promise.then(data => {
    console.log(data); //輸出 haha
}, err=> {
    console.log(err);
})

// 屢次調用
promise.then(data => {
    console.log(data); //輸出 haha
}, err=> {
    console.log(err);
})複製代碼

上面能夠注意到, new Promise中的改變狀態操做咱們使用的是同步,那若是是異步呢,咱們平時遇到的基本都是異步操做,該如何解決?promise

這裏咱們須要在構造函數中存放兩個數組,分別保存成功回調和失敗的回調
由於能夠then屢次,因此須要將這些函數放在數組中
代碼以下:bash

// 這裏咱們建立了一個構造函數 參數就是執行器
function Promise(exector) {
    // 這裏咱們將value 成功時候的值 reason失敗時候的值放入屬性中
    let self = this;
    // 這裏咱們加入一個狀態標識
    this.status = 'pending';
    this.value = undefined;
    this.reason = undefined;
    // 存儲then中成功的回調函數
    this.onResolvedCallbacks = [];
    // 存儲then中失敗的回調函數
    this.onRejectedCallbacks = [];
    
    // 成功執行
    function resolve(value) {
        // 判斷是否處於pending狀態
        if (self.status === 'pending') {
            self.value = value;
            // 這裏咱們執行以後須要更改狀態
            self.status = 'resolved';
            // 成功以後遍歷then中成功的全部回調函數
            self.onResolvedCallbacks.forEach(fn => fn());
        }
    }
    
    // 失敗執行
    function reject(reason) {
        // 判斷是否處於pending狀態
        if (self.status === 'pending') {
            self.reason = reason;
            // 這裏咱們執行以後須要更改狀態
            self.status = 'rejected';
            // 成功以後遍歷then中失敗的全部回調函數
            self.onRejectedCallbacks.forEach(fn => fn());
        }
    }
    
    // 這裏對異常進行處理
     try {
        exector(resolve, reject);
    } catch(e) {
        reject(e)
    }
}


// then 改造

Promise.prototype.then = function(onFulfilled, onRejected) {
    // 獲取下this
    let self = this;
    if (this.status === 'resolved') {
        onFulfulled(self.value);
    }
    
    if (this.status === 'rejected') {
        onRejected(self.reason);
    }
    
    // 若是異步執行則位pending狀態
    if(this.status === 'pending') {
        // 保存回調函數
        this.onResolvedCallbacks.push(() => {
            onFulfilled(self.value);
        })

        this.onRejectedCallbacks.push(() => {
            onRejected(self.reason)
        });
    }
}


// 這裏咱們能夠再次實驗

let promise = new Promise((resolve, reject) => {
    setTimeout(() => {
        if(Math.random() > 0.5) {
            resolve('成功');
        } else {
            reject('失敗');
        }
    })
})

promise.then((data) => {
    console.log('success' + data);
}, (err) => {
    console.log('err' + err);
})
複製代碼

咱們都熟悉Jquery,它的鏈式調用是用rerun this來作的,但是這裏卻不行,緣由文章末尾再解釋。 咱們採起返回一個新的promise對象來實現鏈式調用. 意思也就是p.then()返回一個新promise對象。dom

//給這個函數加個返回值,返回值就是一個新new的promise對象
Promise.prototype.then = function (onFulFilled, onRejected) {
    let p2 = new Promise((resolve, reject) => {});
    
    if (this.status === 'pending') {
        this.onFulFilledCallbacks.push(() => {
            onFulFilled(this.value)
        }); 
        this.onRejectedCallbacks.push(() => {
            onRejected(this.reason)
        })
    } else if (this.status === 'resolved') {
        onFulFilled(this.value);
    } else if (this.status === 'rejected') {
        onRejected(this.reason);
    }
    return p2;
}
複製代碼

  • 加了兩行代碼,咱們測試一下輸出結果:
    任務執行完了
    第一個成功回調
  • 根據上一次咱們所講的能夠總結出以下結論:真正觸發onFulFilledCallbacks裏所存儲的回調函數只有兩個地方:
  1. resolve被調用的時候.(resolve是用戶調用的,由於用戶固然知道哪一種邏輯算任務成功,哪一種邏輯算任務失敗,例如咱們上一章的例子,隨機數大於5我就認爲是成功的.)
  2. then被調用的時候.(then也是用戶調用的,promise裏的任務執行完了後要作啥)

參考:

手寫一個本身的Promise
異步

最最最通俗易懂的promise手寫系列
函數

相關文章
相關標籤/搜索