【磨破嘴皮】手写完整功能Promise

Promise API

要写一个Promise, 需要简单熟悉下Promsie的Api,与Promise的状态

状态

对于表示一个异步操作的最终完成或失败以及其他结果,共有三个状态

  • pending 初始状态、既没有被兑现也没有拒绝
  • fulfilled 操作成功完成
  • rejected 操作失败

Api

  • all 接收一个Iterable类,返回一个promise实例,所有的promise完成,或某一个promise 被拒绝,都会立即抛出错误
  • allSettled 接收一个Iterable类、只有当传入的所有promise,都敲定(rejected, fullfilled)才将返回的promise兑现,并返回所有promise的结果对象数组
  • any 接收一个Iterable类,只要可迭代对象中的某一个promise被兑现(fullfilled)就会立即兑现返回的promise,仅当可迭代对象中所有的Promise都被拒绝(rejected),返回的promise对象才会修改为rejected
  • race 接收一个Iterable类,只要可迭代对象中其中一个被兑现(fullfilled)或者被拒绝(rejected)返回的promise就会立即被兑现或拒绝
  • reject 传入拒绝原因、立即返回一个被拒绝状态的promise对象
  • resolve 传入兑现结果,立即返回一个被兑现的promise对象,如果传入的是thanable则会采用thenalbe的最终状态

如何在resolve执行时,调用then方法

这里利用了一个简单的发布订阅模式,发布了成功失败两个事件因此:.then传入的两个参数可以理解为成功与失败的订阅,resolve,rejcet则是成功或失败事件的派发

.then其实是将传入的回调存储在数组中,待resolve调用后,遍历数组执行

.then

MyPromise.prototype.then = function (onFulfilled, onRejected) {
    if (this.status === PENDING) {
        // 成功的回调
        this.onFulfilledCallbacks.push(realOnFulfilled);
        // 失败的回调
        this.onRejectedCallbacks.push(realOnRejected);
    }
}

Resolve 与reject方法遍历执行.then的回调

 function resolve(value) {
        if (that.status === PENDING) {
            that.status = FULFILLED;
            that.value = value
            that.onFulfilledCallbacks.forEach((callback) => {
                callback(that.value)
            })
        }
    }
    function reject(reason) {
        if (that.status === PENDING) {
            that.status = REJECTED
            that.reason = reason
            that.onRejectedCallbacks.forEach((callback) => {
                callback(that.reason)
            })
        }
    }

如何让.then方法的回调异步执行

onFulfilledonRejected 方法异步执行,且应该在 then 方法被调用的那一轮事件循环之后的新执行栈中执行。所以在我们执行onFulfilledonRejected的时候都应该包到setTimeout里面去。

function timeoutWrapper(fn) {
    return function (...args) {
        const time = setTimeout(()=>{
            fn(...args)
            clearTimeout(time)
        }, 0)
    }
}
_fullfille = timeoutWrapper(()=>{
            try {
                // 如果传入的onFulfilled是一个值,则需要将这个值传递给下一个.then
                if (typeof onFulfilled !== 'function') {
                    _resolve(this.value)
                } else {
                    _returnVal = realOnFulfilled(this.value)
                    _resolve(this.value)
                }
            } catch (e) {
                _reject(e)
            }
 })

如何获取回调返回的值,并兼容thenable

.then的回调中,返回的值会传递给下一个.then,执行then回调时,使用_returnVal接收它返回的值,并按值的类型调用resolvePromise方法,决定不同的处理方式

  • 值为promise则执行传入的promise.then并递归执行resolvePromise,处理这个promise中.then会调的返回值
  • 当返回值为对象时,则要考虑thenable的情况,如果只是普通对象则作传递给下一个.then的回调作为入参
// 将传入的方法包裹在 timeout中执行

MyPromise.prototype.then = function (onFulfilled, onRejected) {
    let realOnFulfilled = onFulfilled
    const that = this
    if (typeof realOnFulfilled !== 'function') {
        realOnFulfilled = function (value) {
            return value
        }
    }
    let realOnRejected = onRejected
    if (typeof realOnRejected !== 'function') {
        realOnRejected = function (reason) {
            if (reason instanceof Error) {
                throw reason;
            } else {
                throw new Error(reason)
            }
        }
    }
    // 失败
    let _reject;
    // 成功
    let _resolve;
    // 返回值
    let _returnVal;
    // 按照promise的状态,执行对应的兑现方法
    let _fullfill;
    const _promise = new MyPromise(function (resolve, reject) {
        _reject = reject;
        _resolve = resolve
    })
    // 如果一个promise已经被兑现
    if (this.status === FULFILLED) {
        _fullfill = timeoutWrapper(()=>{
            try {
                // 如果传入的onFulfilled是一个值,则需要将这个值传递给下一个.then
                if (typeof onFulfilled !== 'function') {
                    _resolve(this.value)
                } else {
                    _returnVal = realOnFulfilled(this.value)
                    _resolve(this.value)
                }
            } catch (e) {
                _reject(e)
            }
        })
    }
    // 如果promise已经被拒绝
    if (this.status === REJECTED) {
        _fullfill = timeoutWrapper(()=>{
            try {
                if (typeof onRejected !== 'function') {
                    _reject(this.reason)
                } else {
                    _returnVal = realOnRejected(this.reason)
                    _resolve()
                }
            } catch (e) {
                _reject(e)
            }
        })
    }
    // 如果promise还在等待中,则将回调暂存到数组中
    if (this.status === PENDING) {
        this.onFulfilledCallbacks.push(timeoutWrapper(function () {
            try {
                _returnVal = realOnFulfilled(that.value)
            } catch (e) {
                _reject(e)
            }
        }));
        this.onRejectedCallbacks.push(timeoutWrapper(function () {
            try {
                _returnVal = realOnRejected(that.reason)
            } catch (e) {
                _reject(e)
            }
        }));
    }
    // 当promise已经被兑现时,直接执行回调
    _fullfill && _fullfill()
    // 处理回调返回的值
    resolvePromise(_promise, _returnVal, _resolve, _reject)
    return _promise
}
function resolvePromise(promise, x, resolve, reject) {
    // 如果 promise 和 x 指向同一对象,以 TypeError 为据因拒绝执行 promise
    // 这是为了防止死循环
    if (promise === x) {
        return reject(new TypeError('The promise and the return value are the same'));
    }

    if (x instanceof MyPromise) {
        x.then(function (y) {
            resolvePromise(promise, y, resolve, reject)
        }, reject)
    } else if (typeof x === 'object' || typeof x === 'function') {
        if (x === null) {
            return resolve(x);
        }
        let then;
        let called = false;

        // thenable 兼容
        try {
            then = x.then;
        } catch (e) {
            return reject(error);
        }

        if (typeof then === 'function') {
            try {
                then.call(x, function (y) {
                    if (called) return;
                    called = true;
                    resolvePromise(promise, y, resolve, reject);
                }, function (r) {
                    if(called) return;
                    called = true
                    reject(r)
                })
            } catch(e) {
                if(called) return;
                reject(e)
            }
        } else {
            resolve(x)
        }

    }
}

.catch实现

catch将在promise被reject或方法执行出错时执行

MyPromise.prototype.catch = function (onRejected) {
    this.then(null, onRejected)
}

.finally实现

无论回调兑现或拒绝,始终执行一次

MyPromise.prototype.finally = function (callback) {
    return this.then(function (val) {
        return MyPromise.resolve(callback()).then(function () {
            return val
        })
    }, function() {
        return MyPromise.resolve(callback()).then(function () {
            throw error
          });
    })
}

Promise.resolve实现

resolve接收兑现的值,并直接返回一个被兑现的promise

MyPromise.resolve = function (parameter) {
    if(parameter instanceof MyPromise) {
        return parameter
    }
    return new MyPromise((resolve)=>{
        resolve(parameter)
    })
}

Promise.reject实现

MyPromise.reject = function (reason) {
    return new MyPromise((_, reject)=>{
        reject(reason)
    })
}

Promise.all实现

接收一个可迭代的promise集合,或数组,当所有的promise被兑现,或任意一个promise被拒绝时立即兑现返回的promise

MyPromise.all = function(promiseList) {
    const resPromise = new MyPromise(function (resolve, reject) {
        let count = 0
        let result = []
        let length = promiseList.length
        if(length === 0) {
            return resolve()
        }
        promiseList.forEach(function(promise, index) {
            MyPromise.resolve(promise).then(function(value) {
                count++;
                result[index] = value;
                if(count === length) {
                    resolve(result)
                }
            }, function (reason) {
                reject(reason)
            })
        })
    })
    return resPromise
}

Promise.race实现

传入的promise集合中,任意一个被兑现或拒绝。则立即兑现返回的promise

MyPromise.race = function (promiseList) {
    const resPromise = new MyPromise(function (resolve, reject) {
        const maxCount = promiseList.length
        let count = promiseList.length
        if(maxCount === 0) {
            return resolve()
        } else {
            while(count--) {
                const index = maxCount - count
                MyPromise.resolve(promiseList[index]).then((value)=> {
                    return resolve(value)
                }, function(reason) {
                    return reject(reason)
                })
            }
        }
    })
    return resPromise
}

Promise.allSettled

当传入的promise集合全部兑现或拒绝,才兑现返回的promise

MyPromise.allSettled = function (promiseList) {
    return new MyPromise(function (resolve) {
        let length = promiseList.length;
        let result = []
        let count = 0
        if(length === 0) {
            return resolve()
        } else {
            for(let i = 0; i < length; i++) {
                const currentPromise = MyPromise.resolve(promiseList[i])
                currentPromise.then((value)=>{
                    count++
                    result[i] = {
                        status: FULFILLED,
                        value
                    }
                    if(count === length) {
                        return resolve(result)
                    }
                }, (reason)=>{
                    count++
                    result[i] = {
                        status: REJECTED,
                        reason
                    }
                    if(count === length) {
                        return resolve(result)
                    }
                })
            }
        }
    })
}

完整代码

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

function MyPromise(fn) {
    let that = this
    this.status = PENDING;
    this.value = null;
    this.reason = null;
    this.onFulfilledCallbacks = []
    this.onRejectedCallbacks = []
    function resolve(value) {
        if (that.status === PENDING) {
            that.status = FULFILLED;
            that.value = value
            that.onFulfilledCallbacks.forEach((callback) => {
                callback(that.value)
            })
        }
    }
    function reject(reason) {
        if (that.status === PENDING) {
            that.status = REJECTED
            that.reason = reason
            that.onRejectedCallbacks.forEach((callback) => {
                callback(that.reason)
            })
        }
    }
    try {
        fn(resolve, reject)
    } catch (e) {
        reject(e)
    }
}
function resolvePromise(promise, x, resolve, reject) {
    // 如果 promise 和 x 指向同一对象,以 TypeError 为据因拒绝执行 promise
    // 这是为了防止死循环
    if (promise === x) {
        return reject(new TypeError('The promise and the return value are the same'));
    }

    if (x instanceof MyPromise) {
        x.then(function (y) {
            resolvePromise(promise, y, resolve, reject)
        }, reject)
    } else if (typeof x === 'object' || typeof x === 'function') {
        if (x === null) {
            return resolve(x);
        }
        let then;
        let called = false;

        // thenable 兼容
        try {
            then = x.then;
        } catch (e) {
            return reject(error);
        }

        if (typeof then === 'function') {
            try {
                then.call(x, function (y) {
                    if (called) return;
                    called = true;
                    resolvePromise(promise, y, resolve, reject);
                }, function (r) {
                    if(called) return;
                    called = true
                    reject(r)
                })
            } catch(e) {
                if(called) return;
                reject(e)
            }
        } else {
            resolve(x)
        }

    }
}
function timeoutWrapper(fn) {
    return function () {
        const time = setTimeout(()=>{
            fn()
            clearTimeout(time)
        }, 0)
    }
}

// 发布订阅实现then回调调用时机
// then的返回值 。then的返回值必须是一个promise
// 如果 onFulfilled 或者 onRejected 返回一个值 x ,则运行下面的 Promise 解决过程:[[Resolve]](promise2, x)。
MyPromise.prototype.then = function (onFulfilled, onRejected) {
    let realOnFulfilled = onFulfilled
    const that = this
    if (typeof realOnFulfilled !== 'function') {
        realOnFulfilled = function (value) {
            return value
        }
    }
    let realOnRejected = onRejected
    if (typeof realOnRejected !== 'function') {
        realOnRejected = function (reason) {
            if (reason instanceof Error) {
                throw reason;
            } else {
                throw new Error(reason)
            }
        }
    }
    let _reject;
    let _resolve;
    let _returnVal;
    let _fullfill;
    const _promise = new MyPromise(function (resolve, reject) {
        _reject = reject;
        _resolve = resolve
    })
    // .then的返回值必须为一个promise
    if (this.status === FULFILLED) {
        _fullfill = timeoutWrapper(()=>{
            try {
                // 如果传入的onFulfilled是一个值,则需要将这个值传递给下一个.then
                if (typeof onFulfilled !== 'function') {
                    _resolve(this.value)
                } else {
                    _returnVal = realOnFulfilled(this.value)
                    _resolve(this.value)
                }
            } catch (e) {
                _reject(e)
            }
        })
    }
    if (this.status === REJECTED) {
        _fullfill = timeoutWrapper(()=>{
            try {
                if (typeof onRejected !== 'function') {
                    _reject(this.reason)
                } else {
                    _returnVal = realOnRejected(this.reason)
                    _resolve()
                }
            } catch (e) {
                _reject(e)
            }
        })
    }
    if (this.status === PENDING) {
        this.onFulfilledCallbacks.push(timeoutWrapper(function () {
            try {
                _returnVal = realOnFulfilled(that.value)
            } catch (e) {
                _reject(e)
            }
        }));
        this.onRejectedCallbacks.push(timeoutWrapper(function () {
            try {
                _returnVal = realOnRejected(that.reason)
            } catch (e) {
                _reject(e)
            }
        }));
    }
    _fullfill && _fullfill()
    resolvePromise(_promise, _returnVal, _resolve, _reject)
    return _promise
}
MyPromise.prototype.catch = function (onRejected) {
    this.then(null, onRejected)
}
MyPromise.prototype.finally = function (callback) {
    return this.then(function (val) {
        return MyPromise.resolve(callback()).then(function () {
            return val
        })
    }, function() {
        return MyPromise.resolve(callback()).then(function () {
            throw error
          });
    })
}
MyPromise.resolve = function (parameter) {
    if(parameter instanceof MyPromise) {
        return parameter
    }
    return new MyPromise((resolve)=>{
        resolve(parameter)
    })
}
MyPromise.reject = function (reason) {
    return new MyPromise((_, reject)=>{
        reject(reason)
    })
}
MyPromise.all = function(promiseList) {
    const resPromise = new MyPromise(function (resolve, reject) {
        let count = 0
        let result = []
        let length = promiseList.length
        if(length === 0) {
            return resolve()
        }
        promiseList.forEach(function(promise, index) {
            MyPromise.resolve(promise).then(function(value) {
                count++;
                result[index] = value;
                if(count === length) {
                    resolve(result)
                }
            }, function (reason) {
                reject(reason)
            })
        })
    })
    return resPromise
}
MyPromise.race = function (promiseList) {
    const resPromise = new MyPromise(function (resolve, reject) {
        const maxCount = promiseList.length
        let count = promiseList.length
        if(maxCount === 0) {
            return resolve()
        } else {
            while(count--) {
                const index = maxCount - count
                MyPromise.resolve(promiseList[index]).then((value)=> {
                    return resolve(value)
                }, function(reason) {
                    return reject(reason)
                })
            }
        }
    })
    return resPromise
}
MyPromise.allSettled = function (promiseList) {
    return new MyPromise(function (resolve) {
        let length = promiseList.length;
        let result = []
        let count = 0
        if(length === 0) {
            return resolve()
        } else {
            for(let i = 0; i < length; i++) {
                const currentPromise = MyPromise.resolve(promiseList[i])
                currentPromise.then((value)=>{
                    count++
                    result[i] = {
                        status: FULFILLED,
                        value
                    }
                    if(count === length) {
                        return resolve(result)
                    }
                }, (reason)=>{
                    count++
                    result[i] = {
                        status: REJECTED,
                        reason
                    }
                    if(count === length) {
                        return resolve(result)
                    }
                })
            }
        }
    })
}

// 测试MyPromise类的resolve方法
const testResolve = function() {
    const promise = MyPromise.resolve('Resolved!')
    promise.then((data) => {
        console.log('resolve方法测试结果:', data)
    })
}

// 测试MyPromise类的reject方法
const testReject = function() {
    const promise = MyPromise.reject('Rejected!')
    promise.catch((err) => {
        console.log('reject方法测试结果:', err)
    })
}

// 测试MyPromise类的then方法
const testThen = function() {
    const promise = new MyPromise((resolve, reject) => { setTimeout(() => { resolve('Resolved!'); }, 1000); })
    promise.then((data) => {
        console.log('then方法测试结果:', data)
    })
}

// 测试MyPromise类的catch方法
const testCatch = function() {
    const promise = new MyPromise((resolve, reject) => { setTimeout(() => { reject('Rejected!'); }, 1000); })
    promise.catch((err) => {
        console.log('catch方法测试结果:', err)
    })
}

// 测试MyPromise类的finally方法
const testFinally = function() {
    const promise = new MyPromise((resolve, reject) => { setTimeout(() => { resolve('Resolved!'); }, 1000); })
    promise.finally(() => {
        console.log('finally方法测试结果:执行完毕')
    })
}

// 测试MyPromise类的all方法
const testAll = function() {
    const promise1 = new MyPromise((resolve, reject) => { setTimeout(() => { resolve('Promise 1 Resolved!'); }, 1000); })
    const promise2 = new MyPromise((resolve, reject) => { setTimeout(() => { resolve('Promise 2 Resolved!'); }, 2000); })
    const promise3 = new MyPromise((resolve, reject) => { setTimeout(() => { resolve('Promise 3 Resolved!'); }, 3000); })
    const promiseList = [promise1, promise2, promise3]
    MyPromise.all(promiseList).then((data) => {
        console.log('all方法测试结果:', data)
    })
}

// 测试MyPromise类的race方法
const testRace = function() {
    const promise1 = new MyPromise((resolve, reject) => { setTimeout(() => { resolve('Promise 1 Resolved!'); }, 1000); })
    const promise2 = new MyPromise((resolve, reject) => { setTimeout(() => { resolve('Promise 2 Resolved!'); }, 2000); })
    const promise3 = new MyPromise((resolve, reject) => { setTimeout(() => { resolve('Promise 3 Resolved!'); }, 3000); })
    const promiseList = [promise1, promise2, promise3]
    MyPromise.race(promiseList).then((data) => {
        console.log('race方法测试结果:', data)
    })
}

// 测试MyPromise类的allSettled方法
const testAllSettled = function() {
    const promise1 = new MyPromise((resolve, reject) => { setTimeout(() => { resolve('Promise 1 Resolved!'); }, 1000); })
    const promise2 = new MyPromise((resolve, reject) => { setTimeout(() => { reject('Promise 2 Rejected!'); }, 2000); })
    const promise3 = new MyPromise((resolve, reject) => { setTimeout(() => { resolve('Promise 3 Resolved!'); }, 3000); })
    const promiseList = [promise1, promise2, promise3]
    MyPromise.allSettled(promiseList).then((data) => {
        console.log('allSettled方法测试结果:', data)
    })
}

// 执行所有测试
testResolve()
testReject()
testThen()
testCatch()
testFinally()
testAll()
testRace()
testAllSettled()
全部评论

相关推荐

不愿透露姓名的神秘牛友
11-21 17:16
科大讯飞 算法工程师 28.0k*14.0, 百分之三十是绩效,惯例只发0.9
点赞 评论 收藏
分享
11-15 17:19
湖南大学 Java
成果成果成果果:这是哪个公司的hr,这么离谱吗,我没见过用性别卡技术岗的,身边女性同学拿大厂offer的比比皆是
点赞 评论 收藏
分享
勤奋努力的椰子这就开摆:美团骑手在美团工作没毛病
投递美团等公司10个岗位
点赞 评论 收藏
分享
点赞 收藏 评论
分享
牛客网
牛客企业服务