# 从零开始实现一个Promise

先看看promise的一个基本用法:

function task1() {
    return new Promise(function(resolve, reject) {
        console.log("task1");
    })
}

function task2() {
    return new Promise(function(resolve, reject) {
        console.log("task2");
    })
}

function task3() {
    return new Promise(function(resolve, reject) {
        console.log("task3");
    })
}

// 调用函数
task1()
    .then(task2())
    .then(task3())

创建Promise实例时,我们传入了一个函数,函数的两个参数(resolve/reject)分别将Promise的状态变为成功态和失败态。首先搭建出基本骨架:

const PENDING = 'pending'
const RESOLVED = 'resolved'
const REJECTED = 'rejected'

class Promise {
    constructor(executor) {
        this.state = PENDING
        this.value = undefined
        this.reason = undefined
    }

    resolve(value) {

    }

    reject(reason) {

    }
}

Promise.prototype.then = (onFullFilled, onRejected) => {

}

module.exports = Promise

Promise实例中state保存它的状态,分为3种:等待态(pending)成功态(resolved)和失败态(rejected)。因为Promise也可以通过.then进行调用,因此在Promise的原型上绑定了then方法。

接下来分别实现:

  1. 当实例化Promise时,构造函数中就要马上调用传入的executor函数执行
  2. 完成resolve和reject方法,已经是成功态或是失败态不可再更新状态
  3. 实现原型上的then方法,完成Promise.prototype.then函数
const PENDING = 'pending'
const RESOLVED = 'resolved'
const REJECTED = 'rejected'

class Promise {
    constructor(executor) {
        this.state = PENDING
        this.value = undefined
        this.reason = undefined
        executor(this.resolve, this.reject)
    }

    resolve(value) {
        if (this.state === PENDING) {
            this.value = value
            this.state = RESOLVED
        }
    }

    reject(reason) {
        if (this.state === PENDING) {
            this.reason = reason
            this.state = REJECTED
        }
    }
}

Promise.prototype.then = (onFullFilled, onRejected) => {
    if (this.state === RESOLVED) {
        if (typeof onFullFilled === 'function') {
            onFullFilled(this.value)
        }
    }
    if (this.state === REJECTED) {
        if (typeof onRejected === 'function') {
            onRejected(this.reason)
        }
    }
}

module.exports = Promise

目前已经完成了Promise的基本功能,接下来解决异步问题。因为此时的代码还不支持Promise种传入异步函数。 我们可以创建两个数组onFulfilledFunc、onRejectedFunc 分别存放成功的回调和失败的回调,当then方法执行时,若状态还在等待态(pending),将回调函数依次放入数组中,这样在resolve和reject方法中可以分别将数组中的回调函数依次执行(resolve中执行onFulfilledFunc的所有方法,reject中执行onRejectedFunc的所有方法),具体实现如下:

const PENDING = 'pending'
const RESOLVED = 'resolved'
const REJECTED = 'rejected'

class Promise {
    constructor(executor) {
        this.state = PENDING
        this.value = undefined
        this.reason = undefined
        this.onFulfilledFunc = []; //保存成功回调
        this.onRejectedFunc = []; //保存失败回调
        executor(this.resolve, this.reject)
    }

    resolve(value) {
        if (this.state === PENDING) {
            this.value = value
            this.onFulfilledFunc.forEach(fn => fn(value))
            this.state = RESOLVED
        }
    }

    reject(reason) {
        if (this.state === PENDING) {
            this.reason = reason
            this.onRejectedFunc.forEach(fn => fn(reason))
            this.state = REJECTED
        }
    }
}

Promise.prototype.then = (onFullFilled, 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 onFullFilled === 'function') {
            onFullFilled(this.value)
        }
    }
    if (this.state === REJECTED) {
        if (typeof onRejected === 'function') {
            onRejected(this.reason)
        }
    }
}

module.exports = Promise

现在我们测试实现的Promise类:

function task1() {
    return new Promise(function(resolve, reject) {
        console.log("task1");
    })
}

function task2() {
    return new Promise(function(resolve, reject) {
        console.log("task2");
    })
}

function task3() {
    return new Promise(function(resolve, reject) {
        console.log("task3");
    })
}

// 调用函数
task1()
    .then(task2())
    .then(task3())

-----------------------
output:
task1
task2
task3

不过目前的Promise还存在一些问题:

  1. 尚不支持then链式调用
  2. 异常捕获
  3. all、race等方法实现

接下来实现链式调用和异常捕获:

  • 每个then方法都返回一个新的Promise对象(原理的核心)
  • 如果then方法中显示地返回了一个Promise对象就以此对象为准,返回它的结果
  • 如果then方法中返回的是一个普通值(如Number、String等)就使用此值包装成一个新的Promise对象返回。
  • 如果then方法中没有return语句,就视为返回一个用Undefined包装的Promise对象
  • 若then方法中出现异常,则调用失败态方法(reject)跳转到下一个then的onRejected
  • 如果then方法没有传入任何回调,则继续向下传递(值的传递特性)。

修改如下:

  1. 使MyPromise.prototype.then方法返回一个Promise
  2. 实现resolvePromise方法(核心)
  3. 重写MyPromise.prototype.then逻辑
const PENDING = 'pending'
const RESOLVED = 'resolved'
const REJECTED = 'rejected'

class MyPromise {
    constructor(executor) {
        this.state = PENDING
        this.value = undefined
        this.reason = undefined
        this.onFulfilledFunc = []; //保存成功回调
        this.onRejectedFunc = []; //保存失败回调
        executor(this.resolve, this.reject)
    }
    resolve(value) {
        if (this.state === PENDING) {
            this.value = value
            this.onFulfilledFunc.forEach(fn => fn(value))
            this.state = RESOLVED
        }
    }
    reject(reason) {
        if (this.state === PENDING) {
            this.reason = reason
            this.onRejectedFunc.forEach(fn => fn(reason))
            this.state = REJECTED
        }
    }
}

/**
 * 解析then返回值与新Promise对象
 * @param {Object} promise2 新的Promise对象 
 * @param {*} x 上一个then的返回值
 * @param {Function} resolve promise2的resolve
 * @param {Function} reject promise2的reject
 */
function resolvePromise(promise2, x, resolve, reject) {
    if (promise2 === x) {
        reject(new TypeError('Promise发生了循环引用'));
    }
    if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
        //可能是个对象或是函数
        try {
            let then = x.then;
            if (typeof then === 'function') {
                let y = then.call(x, (y) => {
                    //递归调用,传入y若是Promise对象,继续循环
                    resolvePromise(promise2, y, resolve, reject);
                }, (r) => {
                    reject(r);
                });
            } else {
                resolve(x);
            }
        } catch (e) {
            reject(e);
        }
    } else {
        //是个普通值,最终结束递归
        resolve(x);
    }
}

MyPromise.prototype.then = (onFullfilled, onRejected) => {
    var promise2 = new Promise((resolve, reject) => {})
    var self = this
    if (this.state === PENDING) {
        promise2 = new Promise(function(resolve, reject) {
            if (typeof onFullFilled === 'function') {
                self.onRejectedFunc.push(function() {
                    //x可能是一个promise,也可能是个普通值
                    setTimeout(function() {
                        try {
                            let x = onFullfilled(self.value)
                            resolvePromise(promise2, x, resolve, reject)
                        } catch (err) {
                            reject(err)
                        }
                    });
                })
            }
            if (typeof onRejected === 'function') {
                self.onRejectedFunc.push(function() {
                    //x可能是一个promise,也可能是个普通值
                    setTimeout(function() {
                        try {
                            let x = onRejected(self.reason)
                            resolvePromise(promise2, x, resolve, reject)
                        } catch (err) {
                            reject(err)
                        }
                    });
                })
            }
        })
    }
    if (this.state === RESOLVED) {
        if (typeof onFullFilled === 'function') {
            promise2 = new Promise(function(resolve, reject) {
                //x可能是一个promise,也可能是个普通值
                setTimeout(function() {
                    try {
                        let x = infulfilled(self.value)
                        onFullFilled(promise2, x, resolve, reject)
                    } catch (err) {
                        reject(err)
                    }
                });
            })
        }
    }
    if (this.state === REJECTED) {
        if (typeof onRejected === 'function') {
            promise2 = new Promise(function(resolve, reject) {
                //x可能是一个promise,也可能是个普通值
                setTimeout(function() {
                    try {
                        let x = onRejected(self.reason)
                        resolvePromise(promise2, x, resolve, reject)
                    } catch (err) {
                        reject(err)
                    }
                });
            })
        }
    }
    return promise2
}

module.exports = MyPromise