自己實現一個簡單的 Promise

xieshuning發表於2018-01-15

Promise : Promise 是一個非同步操作返回的物件,用來傳遞非同步操作的訊息。

Promise 的三種狀態:

  • Pending Promise物件例項建立時候的初始狀態
  • Fulfilled 可以理解為成功的狀態
  • Rejected 可以理解為失敗的狀態

使用 Promise :

let promise = new Promise((resolve, reject) => {
   /* 非同步操作 呼叫 resolve / reject */
});
promise.then(Fulfilled,Rejected)
複製程式碼

根據自己對 Promise 的理解,實現一個Promise :

/* 定義初始態 */
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected'

function Promise(executor) {
    let self = this;
    self.status = PENDING;
    self.onResolvedCallbacks = [];
    self.onRejectedCallbacks = [];

    /* 如果 promise的狀態為 pendding,轉為成功態 */
    function resolve(value) {
        if(value != null && value.then && typeof value.then == 'function'){
            return value.then(resolve, reject);
        }
        setTimeout(function () {
            if(self.status == PENDING){
                self.status = FULFILLED;
                self.value = value;
                self.onResolvedCallbacks.forEach(cb=>cb(self.value))
            }
        })
    }

    /* 如果 promise的狀態為 pendding,轉為失敗態 */
    function reject(reason) {
        setTimeout(function () {
            if(self.status == PENDING){
                self.status = REJECTED;
                self.value = reason;
                self.onRejectedCallbacks.forEach(cb=>cb(self.value))
            }
        })
    }

    try{
        executor(resolve,reject);
    }catch(e){
        reject(e)
    }
}複製程式碼

實現 Promise then 方法,解決多層巢狀問題 :

/* 解決多層巢狀 */
function resolvePromise(promise2,x,resolve,reject) {
    if(promise2 === x){
        return reject(new TypeError('迴圈引用~'));
    }
    let called =false;
    if(x instanceof Promise){
        if(x.status == PENDING){
            x.then(function (y) {
                resolvePromise(promise2, y, resolve, reject);
            }, reject)
        }else{
            x.then(resolve, reject);
        }
    }else if(x != null && ((typeof x =='object') || (typeof x == 'function'))){
        try {
            let then = x.then;
            if (typeof  then == 'function') {
                then.call(x, function (y) {
                    if (called) return;
                    called = true;
                    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 {
        resolve(resolve(x));
    }
}

Promise.prototype.then = function (onFulfilled,onRejected) {
    onFulfilled = typeof onFulfilled == 'function'? onFulfilled: function(value){return value};
    onRejected = typeof onRejected == 'function'? onRejected: reason=>{throw reason};
    let self = this;
    let promise2;

    switch (self.status) {
        case FULFILLED:
            promise2 = new Promise(function (resolve, reject) {
                setTimeout(function () {
                    try {
                        let x = (onFulfilled(self.value));
                        resolvePromise(promise2, x, resolve, reject);
                    } catch(e) {
                        reject(e);
                    }
                })
            });
            break;
        case REJECTED:
            promise2 = new Promise(function (resolve, reject) {
                setTimeout(function () {
                    try{
                        let x = onRejected(self.value);
                        resolvePromise(promise2, x, resolve, reject);
                    }catch(e){
                        reject(e)
                    }
                })
            })
            break;
        case PENDING:
            promise2 = new Promise(function (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 = onRejected(self.value);
                        resolvePromise(promise2, x, resolve, reject);
                    }catch(e){
                        reject(e);
                    }

                })
            })
            break;
        default:
            throw 'promise 狀態錯誤';
    }
    return promise2;
}複製程式碼

Promise.catch 的實現 :

/* catch 只傳失敗的回撥 */
Promise.prototype.catch = function(onRejected){
  this.then(null,onRejected);
}複製程式碼

Promise 的幾種常見方法:

Promise.all :

引數 : 接受一個陣列,陣列內是 Promise 例項;

返回值:返回一個 Promise 例項,這個 Promise 例項的狀態取決於引數的 Promise 例項的狀態變化。

Promise.race :

引數 : 接受一個陣列,陣列內是 Promise 例項;

返回值:返回一個 Promise 例項,這個 Promise 例項的狀態取決於引數的 Promise 例項的狀態變化。

Promise.resolve :

返回值:返回一個 Promise 例項,這個 Promise 例項處於 resolve 狀態。

Promise.reject :

返回值:返回一個 Promise 例項,這個 Promise 例項處於 reject 狀態。

相關程式碼 :

function gen(times,cb){
    let result = [],count=0;
    return function(i,data){
        result[i] = data;
        if(++count==times){
            cb(result);
        }
    }
}

Promise.all = function(promises){
    return new Promise(function(resolve,reject){
        let done = gen(promises.length,resolve);
        for(let i=0;i<promises.length;i++){
            promises[i].then(function(data){
                done(i,data);
            },reject);
        }
    });
}

Promise.race = function(promises){
    return new Promise(function(resolve,reject){
        for(let i=0;i<promises.length;i++){
            promises[i].then(resolve,reject);
        }
    });
}

/* 返回一個直接成功的promise */
Promise.resolve = function(value){
    return new Promise(function(resolve){
        resolve(value);
    });
}

/* 返回一個直接失敗的promise */
Promise.reject = function(reason){
    return new Promise(function(resolve,reject){
        reject(reason);
    });
}複製程式碼

PS : 以上只是自己的一些淺顯的理解,如有問題,還請指正。^_^


相關文章