手写一个Promise

什么是promise

  1. 彻底掌握 Promise
  2. Promise异步操作详解
  3. Promise详细用法
  4. 细说 async/await

1. Promise代码基本结构

实例化Promise对象时传入一个函数作为执行器,有两个参数(resolve和reject)分别将结果变为成功态和失败态。当实例化Promise时,构造函数中就要马上调用传入的executor函数执行。我们可以写出基本结构:

function Promise(executor) {
    this.state = 'pending'; //状态
    this.value = undefined; //成功结果
    this.reason = undefined; //失败原因

    function resolve(value) {};
    function reject(reason) {};

    executor(resolve, reject); // 立即执行
}

其中state属性保存了Promise对象的状态,规范中指明,一个Promise对象只有三种状态:等待态(pending)成功态(resolved)和失败态(rejected)。
当一个Promise对象执行成功了要有一个结果,它使用value属性保存;也有可能由于某种原因失败了,这个失败原因放在reason属性中保存。


2. 已经是成功态或是失败态不可再更新状态

规范中规定,当Promise对象已经由pending状态改变为了成功态(resolved)或是失败态(rejected)就不能再次更改状态了。因此我们在更新状态时要判断,如果当前状态是pending(等待态)才可更新:

function Promise(executor) {
	let _this = this;
    this.state = 'pending'; //状态
    this.value = undefined; //成功结果
    this.reason = undefined; //失败原因
    
    function resolve(value) {
        // 只有在pending状态下才可以改变状态
        if (_this.state == 'pending') {
            _this.value = value; // 保存成功结果
            _this.state = 'resolved';
        }
	}
    function reject(reason) {
        // 只有在pending状态下才可以改变状态
        if (_this.state == 'pending') {
            _this.reason = reason; // 保存失败原因
            _this.state = 'rejected';
        }
	}
	
	// 如果executor执行报错,直接执行reject
    try{
      executor(resolve, reject);
    } catch (err) {
      reject(err);
    }
}

以上可以看到,在resolve和reject函数中分别加入了判断,只有当前状态是pending才可进行操作,同时将成功的结果和失败的原因都保存到对应的属性上。之后将state属性置为更新后的状态。


3. then方法的基本实现

每一个Promise实例都有一个then方法,它用来处理异步返回的结果,它是定义在原型上的方法。当Promise的状态发生了改变,不论是成功或是失败都会调用then方法,所以,then方法的实现也很简单,根据state状态来调用不同的回调函数即可:

Promise.prototype.then = function (onFulfilled, onRejected) {
	// 状态为resolved,执行onFulfilled,传入成功的值
    if (this.state === 'resolved') {
        //判断参数类型,是函数执行之
        if (typeof onFulfilled === 'function') {
            onFulfilled(this.value);
        }
    }
    // 状态为rejected,执行onRejected,传入失败的原因
    if (this.state === 'rejected') {
        if (typeof onRejected === 'function') {
            onRejected(this.reason);
        }
    }
};

4. 让Promise支持异步

代码写到这里似乎基本功能都实现了,可是还有一个很大的问题,目前此Promise还不支持异步代码,如果Promise中封装的是异步操作,then方法无能为力:

let p = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve(1);
    }500);
});

p.then(data => console.log(data)); // 没有任何结果

运行以上代码发现没有任何结果,本意是等500毫秒后执行then方法,哪里有问题呢?原因是setTimeout函数使得resolve是异步执行的,有延迟,当调用then方法的时候,此时此刻的状态还是等待态(pending),因此then方法即没有调用onFulfilled也没有调用onRejected。

这个问题如何解决?我们可以参照发布订阅模式,在执行then方法时如果还在等待态(pending),就把回调函数临时寄存到一个数组里,当状态发生改变时依次从数组中取出执行就好了,清楚这个思路我们实现它,首先在类上新增两个Array类型的数组,用于存放回调函数:

function Promise(executor) {
    let _this = this;
    this.state = 'pending';
    this.value = undefined;
    this.reason = undefined;
    this.onFulfilledFunc = [];//保存成功回调
    this.onRejectedFunc = [];//保存失败回调
    //其它代码略...
}

这样当then方法执行时,若状态还在等待态(pending),将回调函数依次放入数组中:

Promise.prototype.then = function (onFulfilled, onRejected) {
    //异步:还处于等待态,此时异步代码还没有走完
    if (this.state == 'pending') {
        if (typeof onFulfilled == 'function') {
            this.onFulfilledFunc.push(onFulfilled); // 保存异步的成功回调
        }
        if (typeof onRejected == 'function') {
            this.onRejectedFunc.push(onRejected); // 保存异步的失败回调
        }
    }
    // 同步
    if (this.state == 'resolved') {
        if (typeof onFulfilled == 'function') {
            onFulfilled(this.value);
        }
    }
    if (this.state == 'rejected') {
        if (typeof onRejected == 'function') {
            onRejected(this.reason);
        }
    }
}

寄存好了回调,接下来就是当状态改变时执行就好了:

function Promise(executor) {
    let _this = this;
    this.state = 'pending' // 状态
    this.value = undefined; // 成功结果
    this.reason = undefined; // 失败原因

    this.onFulfilledFunc = []; // 保存成功回调
    this.onRejectedFunc = []; // 保存失败回调

    function resolve(value) {
        // 只有在pending状态下才可以改变状态
        if (_this.state == 'pending') {
            _this.value = value; // 保存成功结果
            _this.onFulfilledFunc.forEach(fn => { fn(value) });
            _this.state = 'resolved';
        }
    }

    function reject(reason) {
        // 只有在pending状态下才可以改变状态
        if (_this.state == 'pending') {
            _this.reason = reason; // 保存失败原因
            _this.onRejectedFunc.forEach(fn => { fn(reason) });
            _this.state = 'rejected';
        }
    }
    
	// 如果executor执行报错,直接执行reject
    try{
      executor(resolve, reject);
    } catch (err) {
      reject(err);
    }
}

至此,Promise已经支持了异步操作,setTimeout延迟后也可正确执行then方法返回结果。
检验下:

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);
})

通过以上代码,我们实现了一个简易版的promise,说简易版是因为我们的then方法只能调用一次,并没有实现原生promise中的链式调用。

后续更新中…

posted @ 2022-07-20 18:15  猫老板的豆  阅读(110)  评论(0编辑  收藏  举报