手把手教你完成一个Promise

1、constructor

起首我们都晓得Promise 有三个状况,为了轻易我们把它定义成常量

const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';

接下来我们来定义一个类

class MyPromise {
    constructor(executor) {
        //掌握状况,运用了一次以后,接下来的都不被运用
        this.state = PENDING;
        this.value = null;
        this.reason = null;
        
        // 定义resolve函数
        const resolve = value => {
            if (this.state === PENDING) {
                this.value = value;
                this.state = FULFILLED;
            }
        }
        
        // 定义reject函数
        const reject = value => {
            if (this.state === PENDING) {
                this.reason = value;
                this.state = REJECTED;
            }
        }
        
        // executor要领能够会抛出非常,须要捕捉
        try {
             // 将resolve和reject函数给运用者  
            executor(resolve, reject);
        } catch (error) {
            // 假如在函数中抛出非常则将它注入reject中
            reject(error);
        }
    }
}

到这基础比较好明白我简朴申明一下

  • executor:这是实例Promise对象时在组织器中传入的参数,平常是一个function(resolve,reject){}
  • state:`Promise的状况,一开始是默许的pendding状况,每当挪用道resolvereject要领时,就会转变其值,在背面的then`要领中会用到
  • valueresolve回调胜利后,挪用resolve要领内里的参数值
  • reasonreject回调胜利后,挪用reject要领内里的参数值
  • resolve:声明resolve要领在组织器内,经由过程传入的executor要领传入个中,用以给运用者回调
  • reject:声明reject要领在组织器内,经由过程传入的executor要领传入个中,用以给运用者回调

2、then

then就是将Promise中的resolve或许reject的效果拿到,那末我们就能够晓得这里的then要领须要两个参数,胜利回折衷失利回调,上代码!

then(onFulfilled, onRejected) {
    if (this.state === FULFILLED) {
        onFulfilled(this.value)
    }
    if (this.state === REJECTED) {
        onRejected(this.reason)
    }
}

我们来简朴的运转一下测试代码

const mp = new MyPromise((resolve, reject)=> {
    resolve('******* i love you *******');
})
mp.then((suc)=> {
console.log(11111, suc);
}, (err)=> {
console.log('****** 你不爱我了*******', err)
})

// 11111 '******* i love you *******'

如许看着彷佛没有题目,那末我们来尝尝异步函数呢?

const mp = new MyPromise((resolve, reject)=> {
    setTimeout(()=> {
        resolve('******* i love you *******');
    }, 0)
})
mp.then((suc)=> {
console.log(11111, suc);
}, (err)=> {
console.log('****** 你不爱我了*******', err)
})

我们会发明什么也没有打印,那里出题目了呢?原来是由于异步的缘由,当我们实行到then的时刻this. state的值还没发作转变,所以then内里的推断就失效了。那末我们该怎样处理呢?

这就要说回典范得callback 了。来上源码

// 寄存胜利回调的函数
this.onFulfilledCallbacks = [];
// 寄存失利回调的函数
this.onRejectedCallbacks = [];

const resolve = value => {
    if (this.state === PENDING) {
        this.value = value;
        this.state = FULFILLED;
        this.onFulfilledCallbacks.map(fn => fn());
    }
}

const reject = value => {
    if (this.state === PENDING) {
        this.value = value;
        this.reason = REJECTED;
        this.onRejectedCallbacks.map(fn => fn());
    }
}

then内里增加

then(onFulfilled, onRejected) {
    // ... 
    if(this.state === PENDING) {
        this.onFulfilledCallbacks.push(()=> {
            onFulfilled(this.value);
        });
        this.onRejectedCallbacks.push(()=> {
            onRejected(this.value);
        })
    }
}

好了,到这异步的题目处理了,我们再来实行一下适才的测试代码。效果就出来了。到这我们还缺什么呢?

  • 链式挪用
  • 当我们不传参数时应该什么运转

这二个的思绪也都很简朴,链式挪用也就是说我们再返回一个promise的实例就好了。而不传参则就是默许值的题目了。下面来看源码

then(onFulfilled, onRejected) {
    let self = this;
    let promise2 = null;
    //处理onFulfilled,onRejected没有传值的题目
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : y => y
    //由于毛病的值要让背面访问到,所以这里也要跑出个毛病,不然会在以后then的resolve中捕捉
    onRejected = typeof onRejected === 'function' ? onRejected : err => {
        throw err;
    }

    promise2 = new MyPromise((resolve, reject) => {
        if (self.state === PENDING) {
            console.log('then PENDING')
            self.onFulfilledCallbacks.push(() => {
                setTimeout(() => {
                    try {
                        let x = onFulfilled(self.value);
                    console.log(333333, x, typeof x);
                        self.resolvePromise(promise2, x, resolve, reject);
                    } catch (reason) {
                        reject(reason);
                    }
                }, 0)

            });
            self.onRejectedCallbacks.push(() => {
                setTimeout(() => {
                    try {
                        let x = onRejected(self.reason);
                        self.resolvePromise(promise2, x, resolve, reject);
                    } catch (reason) {
                        reject(reason);
                    }
                }, 0);
            });
        }

        if (self.state === FULFILLED) {
            console.log('then FULFILLED')
            setTimeout(() => {
                try {
                    let x = onFulfilled(self.value);
                    self.resolvePromise(promise2, x, resolve, reject);
                } catch (reason) {
                    reject(reason);
                }
            }, 0);
        }

        if (self.state === REJECTED) {
            console.log('then REJECTED')
            setTimeout(() => {
                try {
                    let x = onRejected(self.reason);
                    self.resolvePromise(promise2, x, resolve, reject);
                } catch (reason) {
                    reject(reason);
                }
            })
        }
    });

    return promise2;
}

为何表面要包一层setTimeout?:由于Promise自身是一个异步要领,属于微使命一列,必须得在实行栈实行完了在去取他的值,所以一切的返回值都得包一层异步setTimeout

resolvePromise是什么?:这实际上是官方Promise/A+的需求。由于你的then能够返回任何职,固然包含Promise对象,而假如是Promise对象,我们就须要将他拆解,直到它不是一个Promise对象,取个中的值。

3、resolvePromise

我们直接看代码

resolvePromise(promise2, x, resolve, reject) {
    let self = this;
    let called = false; // called 防备屡次挪用
    //由于promise2是上一个promise.then后的返回效果,所以假如雷同,会致使下面的.then会是同一个promise2,一向都是,没有终点
    //相当于promise.then以后return了本身,由于then会守候return后的promise,致使本身守候本身,一向处于守候
    if (promise2 === x) {
        return reject(new TypeError('轮回援用'));
    }
    //假如x不是null,是对象或许要领
    if (x !== null && (Object.prototype.toString.call(x) === '[object Object]' || Object.prototype.toString.call(x) === '[object Function]')) {
        // x是对象或许函数
        try {
            let then = x.then;

            if (typeof then === 'function') {
                then.call(x, (y) => {
                    // 他人的Promise的then要领能够设置了getter等,运用called防备屡次挪用then要领
                    if (called) return;
                    called = true;
                    // 胜利值y有能够照样promise或许是具有then要领等,再次resolvePromise,直到胜利值为基础范例或许非thenable
                    self.resolvePromise(promise2, y, resolve, reject);
                }, (reason) => {
                    if (called) return;
                    called = true;
                    reject(reason);
                });
            } else {
                if (called) return;
                called = true;
                resolve(x);
            }
        } catch (reason) {
            if (called) return;
            called = true;
            reject(reason);
        }
    } else {
        // x是一般值,直接resolve
        resolve(x);
    }
}
  • 为何要在一开始推断promise2x?:起首在Promise/A+中写了须要推断这两者假如相称,须要抛出非常,我就来解释一下为何,假如这两者相称,我们能够看下下面的例子,第一次p2p1.then出来的效果是个Promise对象,这个Promise对象在被建立的时刻挪用了resolvePromise(promise2,x,resolve,reject)函数,又由于x即是其自身,是个Promise,就须要then要领递归它,直到他不是Promise对象,然则x(p2)的效果还在守候,他却想实行本身的then要领,就会致使守候。
  • 为何要递回去挪用resolvePromise函数?:置信仔细的人已发明了,我这里运用了递归挪用法,起首这是Promise/A+中请求的,其次是营业场景的需求,当我们遇到那种Promiseresolve里的Promiseresolve里又包了一个Promise的话,就须要递归取值,直到x不是Promise对象。

4、catch

//catch要领
catch(onRejected){
  return this.then(null,onRejected)
}

5、finally

finally要领用于指定不论 Promise 对象末了状况怎样,都邑实行的操纵。该要领是 ES2018 引入规范的。

finally(fn) {
    return this.then(value => {
        fn();
        return value;
    }, reason => {
        fn();
        throw reason;
    });
};

6、resolve/reject

人人一建都看到过Promise.resolve()Promise.reject()这两种用法,它们的作用实在就是返回一个Promise对象,我们来完成一下。

static resolve(val) {
    return new MyPromise((resolve, reject) => {
        resolve(val)
    })
}
//reject要领
static reject(val) {
    return new MyPromise((resolve, reject) => {
        reject(val)
    })
}

7、all

all要领能够说是Promise中很经常使用的要领了,它的作用就是将一个数组的Promise对象放在个中,当悉数resolve的时刻就会实行then要领,当有一个reject的时刻就会实行catch,而且他们的效果也是按着数组中的递次来排放的,那末我们来完成一下。

static all(promiseArr) {
    return new MyPromise((resolve, reject) => {
        let result = [];

        promiseArr.forEach((promise, index) => {
            promise.then((value) => {
                result[index] = value;

                if (result.length === promiseArr.length) {
                    resolve(result);
                }
            }, reject);
        });
    });
}

8、race

race方法虽然不经常使用,然则在Promise要领中也是一个能用得上的要领,它的作用是将一个Promise数组放入race中,哪一个先实行完,race就直接实行完,并从then中取值。我们来完成一下吧。

static race(promiseArr) {
    return new MyPromise((resolve, reject) => {
        promiseArr.forEach(promise => {
            promise.then((value) => {
                resolve(value);
            }, reject);
        });
    });
}

9、deferred

static deferred() {
    let dfd = {};
    dfd.promies = new MyPromise((resolve, reject) => {
        dfd.resolve = resolve;
        dfd.rfeject = reject;
    });
    return dfd;
};

什么作用呢?看下面代码你就晓得了


let fs = require('fs')
let MyPromise = require('./MyPromise')
//Promise上的语法糖,为了防备嵌套,轻易挪用
//害处 毛病处理不轻易
function read(){
  let defer = MyPromise.defer()
  fs.readFile('./1.txt','utf8',(err,data)=>{
    if(err)defer.reject(err)
    defer.resolve(data)
  })
  return defer.Promise
}

10、测试

const mp1 = MyPromise.resolve(1);
const mp2 = MyPromise.resolve(2);
const mp3 = MyPromise.resolve(3);
const mp4 = MyPromise.reject(4);

MyPromise.all([mp1, mp2, mp3]).then(x => {
    console.log(x);
}, (err) => {
    console.log('err1', err);
})
MyPromise.race([mp1, mp4, mp2, mp3]).then(x => {
    console.log(x);
}, (err) => {
    console.log('err2', err);
})

var mp = new MyPromise((resolve, reject) => {
    console.log(11111);
    setTimeout(() => {
        resolve(22222);
        console.log(3333);
    }, 1000);
});
mp.then(x => {
    console.log(x);
}, (err) => {
    console.log('err2', err);
})
//11111
//[ 1, 2, 3 ]
//1
//3333
//22222

完全源码请检察

假如有毛病或许不严谨的处所,请务必赋予斧正,非常谢谢。假如喜好或许有所启示,迎接 star,对作者也是一种勉励。

    原文作者:Alan
    原文地址: https://segmentfault.com/a/1190000018694544
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞