Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

从零开始实现一个Promise #25

Open
iloveyou11 opened this issue Apr 30, 2021 · 2 comments
Open

从零开始实现一个Promise #25

iloveyou11 opened this issue Apr 30, 2021 · 2 comments
Labels

Comments

@iloveyou11
Copy link
Owner

先看看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

思考 :如何实现Promise.all() Promise.race()方法?

@iloveyou11 iloveyou11 added the js label Apr 30, 2021
@Eric-art-coder
Copy link

比候策那本前端开发核心知识进阶还讲得更加清晰!

@Eric-art-coder
Copy link

最后一段为什么要区别MyPromise和Promise? @iloveyou11

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants