Promise - 学习如何手写一个Promise

120 阅读4分钟

前言:本文代码比较多只是学习记录,推荐看这位大佬写的文章更加详细具体

Promise的基本规则

  1. Promise 是一个构造函数;

  2. Promise 接收一个函数,这个函数的参数,是(resolve, reject),也要求是函数。

  3. Promise 返回的对象,包含一个 then 函数, then 函数接收两个参数,这两个参数,一般也是函数。

  4. 我们再使用 new 关键字调用 Promise 构造函数时,在结束时 如果正确执行, 调用 resolve 方法,将结果放在 resolve 的参数中执行,这个结果可以在后面的then 中的第一个函数参数(onFulfilled)中拿到; 如果错误执行, 调用 reject 方法,将错误信息放在 reject 的参数中执行,这个结果可以在后面的then 中的第二个函数参数(onRejected)中拿到;

Promise 的 status:

  1. pending
  • 初始的状态,可改变,
  • 一个 promise 在 resolve / reject 前都处于这个状态
  • 我们可以通过调用 resolve 方法或 reject 方法,让这个 promise , 变成 fulfilled/rejected 状态;
  1. fulfilled
  • 不可变状态
  • resolve 之后,变成这个状态,拥有一个 value
  1. rejected
  • 不可变状态
  • reject 之后,变成这个状态,拥有一个 reason

then 函数:

  1. 参数: onFulfilled 必须是函数类型,如果不是,应该被忽略; onRejected 必须是函数类型,如果不是,应该被忽略;

  2. onFulfilled / onRejected 的特性 在 promise 变成 fulfilled / rejected 状态的时候,应该调用 onFulfilled / onRejected; 在 promise 变成 fulfilled / rejected 状态之前,不应该被调用; 只能被调用一次。

具体细节请跳转阮一峰老师的ES6入门

第一版

首先我先根据定义写一个最简单的Promise

 function L0Promise(execute) {
    this.status = 'pending';
    this.value = null;
    this.reason = null;
    // 我们可以通过调用 `resolve` 方法或 `reject` 方法,
    // 让这个 promise , 变成 `fulfilled`/`rejected` 状态;
    const resolve = (value) => {
        if (this.status === 'pending') {
            this.value = value;
            this.status = 'fulfilled';
        }
    };

    const reject = (reason) => {
        if (this.status === 'pending') {
            this.reason = reason;
            this.status = 'rejected';
        }
    };

    execute(resolve, reject);
};
L0Promise.prototype.then = function (onfulfilled, onrejected) {
    //判断是否是函数 不是也给个默认噶不是故意
    onfulfilled = typeof onfulfilled === 'function'
        ? onfulfilled : data => data;
    onrejected = typeof onrejected === 'function'
        ? onrejected : error => { throw error };
    if (this.status === 'fulfilled') {
        onfulfilled(this.value);
    }
    if (this.status === 'rejected') {
        onrejected(this.reason);
    }
};

基本的逻辑写完了 现在让我们来调用一下看看 能不能运行。😀

let promise = new L0Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('data');
    }, 1000)
});
promise.then((data) => {
    console.log(data); //undefined
});

哦吼!!并没有满足我们的预期正常工作。是哪里出问题了呢?🤔

第二版

答案: 熟悉执行机制的小伙伴会发现,当我 resolve 的时候, onfulfilled 函数,已经执行过了。所以,我们需要在一个合适的时间去执行 onfulfilled。换句话说,我们需要在一个合适的时间,去通知 onfulfilled 执行。

是不是很熟悉🤔? 这就是发布订阅模式

Promise 支持这种多次.then 运行 但我们第一版就支持第一次。如何解决呢?

 function L1Promise(execute) {
    this.status = 'pending';
    this.value = null;
    this.reason = null;
    this.onFulfilledArray = [];
    this.onRejectedArray = [];
   
    const resolve = (value) => {
        setTimeout(() => {
            if(this.status === 'pending') {
                this.value = value;
                this.status = 'fulfilled';
                this.onFulfilledArray.forEach(func => func(value));
            }
        })
    };

    const reject = (reason) => {
        setTimeout(() => {
            if(this.status === 'pending') {
                this.reason = reason;
                this.status = 'rejected';
                this.onRejectedArray.forEach(func => func(value));
            }
        })
    };

    execute(resolve, reject);
};

L1Promise.prototype.then = function(onfulfilled, onrejected) {
    onfulfilled = typeof onfulfilled === 'function' 
        ? onfulfilled : data => data;
    onrejected = typeof onrejected === 'function' 
        ? onrejected : error => {throw error};
    if(this.status === 'fulfilled') {
        onfulfilled(this.value);
    }
    if(this.status === 'rejected') {
        onrejected(this.reason);
    }
    if(this.status === 'pending') {
        this.onFulfilledArray.push(onfulfilled);
        this.onRejectedArray.push(onrejected);
    }
};

第三版

不知道大家有没有写过类似的代码

 let promise = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('hello');
    }, 1000)
});
promise.then((data) => {
    console.log(data, 'word');
    return data + ' 咸鱼'
}).then(data => {
    console.log(data);
    return data + ' 咸鱼1'
}).then(data => {
    console.log(data);
})

// hello  word
// hello 咸鱼
// hello 咸鱼 咸鱼1

但我们的第二版却不行 只能支持一个then,这是为啥呢? Promise是如何支持多个.then 的呢? 其实then 应该返回一个promise 然后把这个promise的结果交给第二个then

function L2Promise(execute) {
    this.status = 'pending';
    this.value = null;
    this.reason = null;
    this.onFulfilledArray = [];
    this.onRejectedArray = [];
   
    const resolve = (value) => {
        setTimeout(() => {
            if(this.status === 'pending') {
                this.value = value;
                this.status = 'fulfilled';
                this.onFulfilledArray.forEach(func => func(value));
            }
        })
    };

    const reject = (reason) => {
        setTimeout(() => {
            if(this.status === 'pending') {
                this.reason = reason;
                this.status = 'rejected';
                this.onRejectedArray.forEach(func => func(value));
            }
        })
    };
    //  try catch
    execute(resolve, reject);
};

L2Promise.prototype.then = function(onfulfilled, onrejected) {
    onfulfilled = typeof onfulfilled === 'function' 
        ? onfulfilled : data => data;
    onrejected = typeof onrejected === 'function' 
        ? onrejected : error => {throw error};

    let promise2; // 作为 then 函数的返回值。
    if(this.status === 'fulfilled') {
        return promise2 = new L2Promise((resolve, reject) => {
            setTimeout(() => {
                try {
                    let result = onfulfilled(this.value);
                    resolve(result);
                } catch(e) {
                    reject(e);
                }
            })
        })
    }
    if(this.status === 'rejected') {
        return promise2 = new L2Promise((resolve, reject) => {
            setTimeout(() => {
                try {
                    let result = onrejected(this.reason);
                    resolve(result);
                } catch(e) {
                    reject(e);
                }
            })
        })
        
    }
    if(this.status === 'pending') {
        return promise2 = new L2Promise((resolve, reject) => {
            this.onFulfilledArray.push(() => {
                try {
                    let result = onfulfilled(this.value);
                    resolve(result);
                }
                catch(e) {
                    reject(e);
                }
            })

            this.onRejectedArray.push(() => {
                try {
                    let result = onrejected(this.reason);
                    resolve(result);
                }
                catch(e) {
                    reject(e);
                }
            })
        })
    }
};

总结

其实写到这里 Promise 就基本上差不多了,如果这边都理解了其实面对一般的手写Promise 就都可以了。但是还不太规范如果想要完全符合Promises/A+的规范请大家自行学习了。

还有我只是一个小菜鸡 如果有写的不好的地方,轻喷!!

Promise/A+规范