來,一起來實現一個符合Promise/A+的Promose(1.0.1版本)

css_Machinist發表於2018-03-18

Promose是什麼?(必要的科普)

為解決非同步函式的回撥陷阱,開發社群不斷摸索,終於折騰出 Promise/A+。它的優勢非常顯著:

  1. 不增加新的語法,可以立刻適配幾乎所有瀏覽器
  2. 以佇列的形式組織程式碼,易讀好改
  3. 捕獲異常方案也基本可用

這套方案在迭代中逐步完善,最終被吸收進 ES2015。不僅如此,ES2017 中還增加了 Await/Async,可以用順序的方式書寫非同步程式碼,甚至可以正常丟擲捕獲錯誤,維護同一個棧。可以說徹底解決了非同步回撥的問題。

現在大部分瀏覽器和 Node.js 都已原生支援 Promise,很多類庫也開始返回 Promise 物件,更有各種降級適配策略。Node.js 7+ 則實裝了 Await/Async。


Promose的基本用法

let Mypromise = require('./Promise') // 這個是自己寫的 Promise 函式 底下有程式碼講解
let p1 = new Mypromise(function(resolve,reject){
    resolve('666')
})
p1.then(function(data){
    console.log(data)
},function(err){
    console.log(err)
})複製程式碼

Promise 是一個代理物件,它和原先的非同步操作並無關係。它接受一個“執行器 executor”作為引數,我們把原先要執行的非同步(並非一定要非同步,以後會說明,這裡你可以先不深究)操作放進去。

執行器在 Promise 例項建立後立刻開始執行。執行器自帶兩個引數:resolvereject,這二位都是函式,執行它們會改變 Promise 例項的狀態。

Promise 例項有三個狀態:

  1. pending [待定] 初始狀態,新建立的例項處於這個狀態
  2. fulfilled [實現] 操作成功,在執行器裡呼叫 resolve() 之後,例項切換到這個狀態
  3. rejected [被否決] 操作失敗,在執行器裡呼叫 reject() 之後,例項切換到這個狀態

Promise 例項狀態改變之後,就會觸發後面對應的 .then() 引數裡的函式,繼續執行後續步驟。另外,Promise 的例項狀態只會改變一次,確定為 fulfilledrejected 之一後就不會再變。


我們來看看  Promise函式方面程式碼要怎麼實現:

function Promise(executor) { // executor是一個執行函式
    let self = this;         // 快取this 防止出錯 
    self.status = 'pending';  // 初始化狀態
    self.value = undefined; // 預設成功的值
    self.reason = undefined; // 預設失敗的原因
    function resolve(value) { // 成功狀態
        if (self.status === 'pending') {  // 判斷當前當前狀態
            self.status = 'resolved';  // 把pending 狀態變成 成功的狀態 resolved
            self.value = value;        // 給預設成功狀態 賦值
        }
    }
    function reject(reason) { // 失敗狀態
        if (self.status === 'pending') { // 判斷 Promise 當前狀態 如果是初始化 
            self.status = 'rejected';  // pending 狀態變成 失敗的狀態 rejected
            self.reason = reason;  // 給預設失敗的原因賦值
        }
    }
    try {
        executor(resolve, reject)
    } catch (e) {   //  捕獲執行的時候發生異常,就直接失敗了 當 new 一個 新的 Promise物件 丟擲一個錯誤時候
                    // 如  在 Promise例項裡  throw new Error('錯誤了')
        reject(e);
    }
}

Promise.prototype.then = function(onFulfilled, onReject){    let self = this;   
    if(self.status === 'resolved'){ // 
        onFulfilled(self.value)
    }
    if(self.status === 'rejected'){
        onReject(self.reason)
    }
}
module.exports = Promise;複製程式碼

以上是Promise最基本的實現


我們來試試看執行結果: 當Primose 例項狀態為 resolve(成功)的時候,顯示結果如下

來,一起來實現一個符合Promise/A+的Promose(1.0.1版本)



接下來測試下:  當Primose 例項狀態為 reject(失敗)的時候 顯示如下

來,一起來實現一個符合Promise/A+的Promose(1.0.1版本)

從程式碼結果來看,現在Promise 函式 內部的 ‘ pedding’ 狀態 已經可以 根據 成功和數百的狀態來改變了 

當如果兩個函式同時執行呢!!!如下 

來,一起來實現一個符合Promise/A+的Promose(1.0.1版本)

只會顯示一個結果 誰先執行 狀態就變成誰 不會 同時 執行兩個結果  

現在我們已經初步實現了 Promise 函式 的同步方法了 

接下來繼續 實現其他的 如(多次then ,非同步,Promise (then返回一個新的Promise物件))


promise例項可以多次then,當成功後會將 then 中的成功方法按順序執行,我們可以先講then中成功的回撥存到陣列內,當成功是呼叫成功的陣列即可,失敗的時候也同理

定義兩個接收 成功的陣列 和失敗的陣列 如下:

function Promise(executor) { // executor是一個執行函式
    let self = this;
    self.status = 'pending';
    self.value = undefined; // 預設成功的值
    self.reason = undefined; // 預設失敗的原因
    self.onResolvedCallbacks = []; // 存放then成功的回撥
    self.onRejectedCallbacks = []; // 存放then失敗的回撥
    function resolve(value) { // 成功狀態
        if (self.status === 'pending') {
            self.status = 'resolved';
            self.value = value;
            // 當成功以後, 依次執行成功陣列裡面的函式
            self.onResolvedCallbacks.forEach(function (fn) {
                fn();
            });
        }
    }
    function reject(reason) { // 失敗狀態
        if (self.status === 'pending') {
            self.status = 'rejected';
            self.reason = reason;
            // 當失敗以後, 依次執行失敗陣列裡面的函式
            self.onRejectedCallbacks.forEach(function (fn) {
                fn();
            })
        }
    }
    try {
        executor(resolve, reject)
    } catch (e) { // 捕獲的時候發生異常,就直接失敗了
        reject(e);
    }
}複製程式碼

然後在 then方法中也需要判斷當前的狀態   如果是 pending則需要做下處理

Promise.prototype.then = function(onFulfilled, onReject){
    let self = this;
    if(self.status === 'resolved'){ //
        onFulfilled(self.value)
    }
    if(self.status === 'rejected'){
        onReject(self.reason)
    }
 // 當呼叫then時可能沒成功 也沒失敗    if (self.status === 'pending') {
        // 此時沒有 resolve 也沒有 reject
        self.onResolvedCallbacks.push(function(){
            onFulfilled(self.value)
        })
        self.onRejectedCallbacks.push(function(){
            onReject(self.reason)
        })
    }
}複製程式碼

執行例項程式碼: 結果如下

來,一起來實現一個符合Promise/A+的Promose(1.0.1版本)

用了兩次 then : 顯示都成功了  呼叫多少次then 就顯示多少個成功


鏈式呼叫 jquery,jquery能實現鏈式呼叫靠的就是返回this,promise不能返回this,promise實現鏈式呼叫靠的是返回一個新的promise,如果then中無論是成功的回撥還是失敗的回撥只要返回了結果就會走下一個then中的成功,如果有錯誤走下一個then的失敗

例項程式碼如下:

let p = new Promise(function(resolve,reject){
    resolve();
})
let p2 = p.then(function(){
    throw new Error('錯誤');
})
p2.then(function(){  // 可以返回一個新的 Promise 
},function(err){
    console.log(err); // 這邊輸出錯誤
})複製程式碼

程式碼如下:

Promise.prototype.then = function(onFulfilled, onReject){
    let self = this;
    let promise2; //返回的promise
    if (self.status === 'resolved') {
        promise2 = new Promise(function (resolve, reject) {
            try{
                onFulfilled(self.value);
            }catch (e){
                reject(e)
            }
        })
    }
    if (self.status === 'rejected') {
        promise2 = new Promise(function (resolve, reject) {
            try{
                onReject(self.reason)
            }catch (e){
                reject(e)
            }
        })
    }
    // 當呼叫then時可能沒成功 也沒失敗
    if (self.status === 'pending') {
        promise2 = new Promise(function (resolve, reject) {
            // 此時沒有resolve 也沒有reject
            self.onResolvedCallbacks.push(function () {
                try{
                    onFulfilled(self.value)
                }catch (e){
                    reject(e)
                }
            });
            self.onRejectedCallbacks.push(function () {
                try{
                    onReject(self.reason)
                }catch (e){
                    reject(e)
                }
            });
        })
    }
    return promise2;
}

複製程式碼
如果第一個promise返回一個普通值,會進到下一次then的成功的回撥,如果第一個promise返回了一個promise,需要等待返回的promise執行後的結果傳遞給下一次then中
let p1 = new Promise(function(resolve,reject){
    resolve('333')
})
p1.then(function(data){
    return new Promise(function(resolve,reject){
        setTimeout(function () {
            resolve(100)
        },1000)
    })
},function(err){
    throw new Error('失敗')
}).then(function (data) {
    console.log(data)
},function (err) {
    console.log(err)
})複製程式碼


Promise.prototype.then = function(onFulfilled, onReject){
    let self = this;
    let promise2; //返回的promise
    if (self.status === 'resolved') {
        promise2 = new Promise(function (resolve, reject) {
            // 當成功或者失敗執行時有異常那麼返回的promise應該處於失敗狀態
            // x可能是一個promise 也有可能是一個普通的值
            try{
                let x = onFulfilled(self.value);
                // x可能是別人promise,寫一個方法統一處理
                resolvePromise(promise2, x, resolve, reject);
            }catch (e){
                reject(e)
            }
        })
    }
    if (self.status === 'rejected') {
        promise2 = new Promise(function (resolve, reject) {
            try{
                let x = onRjected(self.reason);
                resolvePromise(promise2, x, resolve, reject);
            }catch (e){
                reject(e)
            }
        })
    }
    // 當呼叫then時可能沒成功 也沒失敗
    if (self.status === 'pending') {
        promise2 = new Promise(function (resolve, reject) {
            // 此時沒有resolve 也沒有reject
            self.onResolvedCallbacks.push(function () {
                try{
                    let x = onFulfilled(self.value);
                    resolvePromise(promise2, x, resolve, reject);
                }catch (e){
                    reject(e)
                }
            });
            self.onRejectedCallbacks.push(function () {
                try{
                    let x = onRjected(self.reason);
                    resolvePromise(promise2, x, resolve, reject);
                }catch (e){
                    reject(e)
                }
            });
        })
    }
    return promise2;
}複製程式碼

接下來實現 resolvePromise 函式

function resolvePromise(promise2, x, resolve, reject) {
    // 有可能這裡返回的x是別人的promise
    // 儘可能允許其他亂寫
    if (promise2 === x) { //這裡應該報一個型別錯誤,有問題
        return reject(new TypeError('迴圈引用了'))
    }
    // 看x是不是一個promise,promise應該是一個物件
    let called; // 表示是否呼叫過成功或者失敗
    if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
        // 可能是promise {},看這個物件中是否有then方法,如果有then我就認為他是promise了
        try { // {then:1}
            let then = x.then;
            if (typeof then === 'function') {
                // 成功
                then.call(x, function (y) {
                    if (called) return
                    called = true
                    // y可能還是一個promise,在去解析直到返回的是一個普通值
                    resolvePromise(promise2, y, resolve, reject)
                }, function (err) { //失敗
                    if (called) return
                    called = true
                    reject(err);
                })
            } else {
                resolve(x)
            }
        } catch (e) {
            if (called) return
            called = true;
            reject(e);
        }
    } else { // 說明是一個普通值1
        resolve(x); // 表示成功了
    }
}複製程式碼
快速實現常用的其他 Promise 方法:
// 捕獲錯誤的方法
Promise.prototype.catch = function (callback) {
    return this.then(null, callback)
}
// 解析全部方法
Promise.all = function (promises) {
    //promises是一個promise的陣列
    return new Promise(function (resolve, reject) {
        let arr = []; //arr是最終返回值的結果
        let i = 0; // 表示成功了多少次
        function processData(index, y) {
            arr[index] = y;
            if (++i === promises.length) {
                resolve(arr);
            }
        }
        for (let i = 0; i < promises.length; i++) {
            promises[i].then(function (y) {
                processData(i, y)
            }, reject)
        }
    })
}
// 只要有一個promise成功了 就算成功。如果第一個失敗了就失敗了
Promise.race = function (promises) {
    return new Promise(function (resolve, reject) {
        for (var i = 0; i < promises.length; i++) {
            promises[i].then(resolve,reject)
        }
    })
}
// 生成一個成功的promise
Promise.resolve = function(value){
    return new Promise(function(resolve,reject){
        resolve(value);
    })
}
// 生成一個失敗的promise
Promise.reject = function(reason){
    return new Promise(function(resolve,reject){
        reject(reason);
    })
}
Promise.defer = Promise.deferred = function () {
    let dfd = {};
    dfd.promise = new Promise(function (resolve, reject) {
        dfd.resolve = resolve;
        dfd.reject = reject;
    });
    return dfd
}複製程式碼

寫在結尾: 第一次寫文章,邏輯有點亂,希望能幫上大家,大家不要噴,有什麼錯誤,請指出,很感謝!肯定會重寫寫一遍,更好的給大家


相關文章