【面试题】吃透Promise?先实现一个再说(包含所有方法)(二)

简介: 【面试题】吃透Promise?先实现一个再说(包含所有方法)(二)

【面试题】吃透Promise?先实现一个再说(包含所有方法)(一):https://developer.aliyun.com/article/1413943

2.3.2是针对x如果是一个promise对象

需要通过对PENDING,FULFILLED,REJECTED3个状态进行 如果x处于pending状态,那么在成功或者失败前,我们需要保存这个状态, 如果x处于fulfilled或者rejected状态我们只需要重新resove或者reject出去即可

2.3.3如果x一个对象或者函数 如果x不是一个对象或者函数,那么为普通值我们直接resolve出去

function resolvePromise(promise2, x, resolve, reject) {
    if (x === promise2) {
        return reject(new TypeError('Chaining cycle detected for promise #<MyPromise>'))
    }
    if ((typeof x === 'object' && x !== null) || typeof x === 'function') {
    } else {
        resolve(x)
    }
}
复制代码

初步的模型已经完成,此时关于前面then的穿透问题可以大致看出来已经解决

  let p1 = new MyPromise((resolve, reject) => {
                resolve('成功')
            })
    .then()
    .then()
    .then(
        (value) => {
            console.log(value) // 成功
        },
        (err) => {
            console.log(err)
        }
    )
复制代码

接下来继续按照规范来,从2.3.1处开始处理对象或者函数的情况 2.3.1说假设x有一个then属性, 2.3.2:在读取属性的时候如果抛出异常则reject出去(Object.defineProerty(x,'then',{get(){throw new Error('err')}})) 则代码如下

function resolvePromise(promise2, x, resolve, reject) {
    if (x === promise2) {
        return reject(new TypeError('Chaining cycle detected for promise #<MyPromise>'))
    }
    if ((typeof x === 'object' && x !== null) || typeof x === 'function') {
        let then = x.then
        try {
        } catch (e) {
            reject(e)
        }
    } else {
        resolve(x)
    }
}
复制代码

2.3.3开始对then进行处理,如果then是一个函数则认为x是一个promise对象,然后调用它(

If then is a function, call it with x as this, first argument resolvePromise, and second argument rejectPromise, where:)并且附带2个参数(函数)处理resolve(参数y)和reject(参数r)这里指的是2.3.3.3.1和2.3.3.3.2 2.3.3.3的意思是如果r和y被多次调用或者对某个函数重复调用,第一次调用优先,其他忽略,因此我们指定一个全局变量called来控制调用

2.3.3.4的意思是如果调用后抛出异常,这个异常可能在调用y或者r函数后造成也可能是在之前就抛出的 因此也需要使用called来控制是否抛出异常 2.3.4以及后面的指的是如果then不是一个函数或者对象,那么确定fulfilled状态resolve出去即可 至此完整resolvePromise函数封装如下

function resolvePromise(promise2, x, resolve, reject) {
    if (x === promise2) {
        return reject(new TypeError('Chaining cycle detected for promise #<MyPromise>'))
    }
    let called = false
    if ((typeof x === 'object' && x !== null) || typeof x === 'function') {
        let then = x.then
        try {
            if (typeof then === 'function') {
                then.call(
                    x,
                    (y) => {
                        if (called) return
                        called = true
                        resolvePromise(promise2, y, resolve, reject)
                    },
                    (r) => {
                        if (called) return
                        called = true
                        reject(r)
                    }
                )
            } else {
                resolve(x)
            }
        } catch (e) {
            if (called) return
            called = true
            reject(e)
        }
    } else {
        resolve(x)
    }
}
复制代码

对了突然想起来对于executor函数中的resolve封装中,如果resolve里面是多层嵌套的promsie对象的话例如这样

let p1 = new Promise((resolve, reject) => {
        resolve(
            new Promise((resolve, reject) => {
                resolve(11)
            })
        )
    }).then((value) => {
        console.log(value)
    })
复制代码

我们需要对resolve的参数做一个提前的判断处理,如果是promsie的实例我们应该调用then方法 添加起来非常简单,代码如下

 const resolve = (value) => {
            if (value instanceof MyPromise) {
                value.then(resolve, reject)
                return
            }
            if (this.status === PENDING) {
                this.status = FULFILLED
                this.value = value
                this.onFulfilledCallbacks.forEach((fn) => fn())
            }
        }
复制代码

至此完整的promise实现代码如下

const [PENDING, FULFILLED, REJECTED] = ['PENDING', 'FULFILLED', 'REJECTED']
class MyPromise {
    constructor(executor) {
        this.status = PENDING
        this.value = undefined
        this.reason = undefined
        this.onFulfilledCallbacks = []
        this.onRejectedCallbacks = []
        const resolve = (value) => {
            if (value instanceof MyPromise) {
                value.then(resolve, reject)
                return
            }
            if (this.status === PENDING) {
                this.status = FULFILLED
                this.value = value
                this.onFulfilledCallbacks.forEach((fn) => fn())
            }
        }
        const reject = (reason) => {
            if (this.status === PENDING) {
                this.status = REJECTED
                this.reason = reason
                this.onRejectedCallbacks.forEach((fn) => fn())
            }
        }
        try {
            executor(resolve, reject)
        } catch (e) {
            reject(e)
        }
    }
    then(onFulfilled, onRejected) {
        onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : (value) => value
        onRejected =
            typeof onRejected === 'function'
                ? onRejected
                : (reason) => {
                      throw reason
                  }
        let promise2 = new MyPromise((resolve, reject) => {
            if (this.status === FULFILLED) {
                setTimeout(() => {
                    try {
                        let x = onFulfilled(this.value)
                        resolvePromise(promise2, x, resolve, reject)
                    } catch (e) {
                        reject(e)
                    }
                }, 0)
            }
            if (this.status === REJECTED) {
                setTimeout(() => {
                    try {
                        let x = onRejected(this.reason)
                        resolvePromise(promise2, x, resolve, reject)
                    } catch (e) {
                        reject(e)
                    }
                }, 0)
            }
            if (this.status === PENDING) {
                this.onFulfilledCallbacks.push(() => {
                    setTimeout(() => {
                        try {
                            let x = onFulfilled(this.value)
                            resolvePromise(promise2, x, resolve, reject)
                        } catch (e) {
                            reject(e)
                        }
                    })
                })
                this.onRejectedCallbacks.push(() => {
                    setTimeout(() => {
                        try {
                            let x = onRejected(this.reason)
                            resolvePromise(promise2, x, resolve, reject)
                        } catch (e) {
                            reject(e)
                        }
                    })
                })
            }
        })
        return promise2
    }    
}
function resolvePromise(promise2, x, resolve, reject) {
    if (x === promise2) {
        return reject(new TypeError('Chaining cycle detected for promise #<MyPromise>'))
    }
    let called = false
    if ((typeof x === 'object' && x !== null) || typeof x === 'function') {
        let then = x.then
        try {
            if (typeof then === 'function') {
                then.call(
                    x,
                    (y) => {
                        if (called) return
                        called = true
                        resolvePromise(promise2, y, resolve, reject)
                    },
                    (r) => {
                        if (called) return
                        called = true
                        reject(r)
                    }
                )
            } else {
                resolve(x)
            }
        } catch (e) {
            if (called) return
            called = true
            reject(e)
        }
    } else {
        resolve(x)
    }
}
复制代码

3.Promise的静态方法以及原型方法

 console.log(Reflect.ownKeys(Promise))
 console.log(Promise.prototype)
复制代码

通过上述代码可查看Promise的静态方法以及原型上的方法

话不多说直接动手开干,都走到这一步了,麻烦亲坚持一下

3.1-Promise.resolve/Promise.reject

这2个方法比较简单,直接调用我们之前封装好的promsie里面的resolve和reject函数即可

   static resolve(value) {
        return new MyPromise((resolve, reject) {
            resolve(value)
        })
    }
    static reject(reason) {
        return new MyPromise((resolve, reject) => {
            reject(reason)
        })
    }
复制代码

3.2-Promise.all

`不了解的同学点击参考MDN-Promise.all

如果参数不是一个可迭代的对象,那么会报错,并且返回的值需要拿promise包裹出去,而且顺序不能变需要注意 代码如下

  static all(promiseArr) {
        let resArr = [],
            idx = 0
        if (!isIterable(promiseArr)) {
            let type = typeof promiseArr
            throw TypeError(`${type} is not a iterable (cannot read property Symbol(Symbol.iterator))
    at Function.all (<anonymous>)`)
        }
        return new Promise((resolve, reject) => {
            promiseArr.map((promise, index) => {
                if (isPromise(promise)) {
                    promise.then((res) => {
                        formatArr(res, index, resolve)
                    }, reject)
                } else {
                    formatArr(promise, index, resolve)
                }
            })
        })
        function formatArr(value, index, resolve) {
            resArr[index] = value
            // if(resArr.length ===promiseArr.length) 在某些时刻不正确,比如数组最后一项先执行完 数组就为[empty,empty,value]
            if (++idx === promiseArr.length) {
                resolve(resArr)
            }
        }
    }
//工具函数封装
function isIterable(value) {
    return value !== null && value !== undefined && typeof value[Symbol.iterator] === 'function'
}
function isPromise(x) {
    if ((typeof x === 'object' && x !== null) || typeof x == 'function') {
        let then = x.then
        return typeof then === 'function'
    }
    return false
}
复制代码

3.3-Promise.allSettled

与promise.all的实现思想大差不多,只不过返回的数组里面包含的表明状态的对象,而且不管是成功或者失败都收集起来

static allSettled(promiseArr) {
        let resArr = [],
            idx = 0
        if (!isIterable(promiseArr)) {
            let type = typeof promiseArr
            throw TypeError(`${type} is not a iterable (cannot read property Symbol(Symbol.iterator))
    at Function.all (<anonymous>)`)
        }
        return new MyPromise((resolve, reject) => {
            if (promiseArr.length === 0) {
                resolve([])
            }
            promiseArr.forEach((promise, index) => {
                if (isPromise(promise)) {
                    promise.then(
                        (value) => {
                            formatArr('fulfilled', value, index, resolve)
                        },
                        (err) => {
                            formatArr('rejected', err, index, resolve)
                        }
                    )
                } else {
                    formatArr('fulfilled', promise, index, resolve)
                }
            })
        })
        function formatArr(status, value, index, resolve) {
            switch (status) {
                case 'fulfilled':
                    resArr[index] = {
                        status,
                        value
                    }
                    break
                case 'rejected':
                    resArr[index] = {
                        status,
                        reason: value
                    }
                    break
                default:
                    break
            }
            if (++idx === promiseArr.length) {
                resolve(resArr)
            }
        }
    }
复制代码

3.4-Promise.race

这个方法也比较简单,race是赛跑的意思,当某一项确定状态后,直接包装成promise出去就好

 static race(promiseArr) {
        if (!isIterable(promiseArr)) {
            let type = typeof promiseArr
            throw TypeError(`${type} is not a iterable (cannot read property Symbol(Symbol.iterator))
    at Function.all (<anonymous>)`)
        }
        return new Promise((resolve, reject) => {
            promiseArr.forEach((promise, index) => {
                if (isPromise(promise)) {
                    promise.then(resolve, reject)
                } else {
                    resolve(promise)
                }
            })
        })ise.then
    }
复制代码

3.4-Promise.prototype.finally

这个方法其实要考虑的因素蛮多,在此我列举出来 finally()方法用于指定不管 Promise 对象最后状态如何,都会执行的操作

finally本质上是then方法的特例。

  1. finally 无论外面的promise状态成功还是失败 都要走 并且回调函数不带参数
  2. 正常走finallu之后then 或者 catch
  3. 如果finally 内部有promise 并且有延时处理,整个finall会等待执行
  4. 如果2个都是成功,取外面结果
  5. 如果外面是成功 里面是失败,取里面失败的结果
  6. 如果外面是失败 里面是成功 取外面失败的结果
  7. 如果外面和里面都是失败,取里面失败的结果
  8. 如果外面成功,里面成功,取外面成功的结果

我们首先要把上一次promise的值保存下来 这样只有当里面是失败的情况下,才取finally内部失败的值,其余取上一个promise的值

 finally(callbacks) {
        return this.then(
            (value) => {
                return MyPromise.resolve(callbacks()).then(() => value)
            },
            (err) => {
                return MyPromise.resolve(callbacks()).then(() => {
                    throw err
                })
            }
        )
    }
复制代码

3.5-Promise.prototype.catch

这个方法比较简单,相当于调用then的第二个回调而已

 catch(callback) {
        return this.then(null.callback)
    }
复制代码

完整带代码 链接 大哥看完麻烦给个star,创作不易,谢谢!!!!!!!!!

参考链接2:promisesaplus.com/ (promiseA+规范)

末尾

本文只是基于romiseA+规范进行实现,掌握可胜任工作以及,中等偏下的面试题,但是与v8引擎下的promise规范还是有很多差异 掌握本文可参考月夕大佬的promise文章 参考链接1:juejin.cn/post/705520… (ECMA规范与V8引擎下的promise)

给大家推荐一个实用面试题库

1、前端面试题库 (面试必备)            推荐:★★★★★

地址:前端面试题库

相关文章
|
1月前
|
前端开发 索引
Promise.all() 方法的参数可以是什么类型?
综上所述,`Promise.all()` 方法的参数类型较为灵活,但无论使用哪种类型的可迭代对象作为参数,其核心的异步操作处理逻辑和成功失败的判断机制都是一致的,都是为了方便地处理多个异步操作的并发执行和结果汇总。
|
1月前
|
存储 前端开发
除了 Promise.all(),还有哪些方法可以处理异步并发操作?
在上述示例中,`concurrentPromises` 函数接受一个Promise数组和最大并发数作为参数,通过手动控制并发执行的Promise数量,实现了对异步操作的并发控制,并在所有Promise完成后返回结果数组。
|
23天前
|
前端开发
Promise.allSettled()方法和Promise.race()方法有什么区别?
`Promise.allSettled()` 提供了一种更全面、更详细的方式来处理多个 `Promise`,而 `Promise.race()` 则更强调速度和竞争。我们需要根据具体的需求来选择使用哪种方法。
|
28天前
|
前端开发
`Promise.all()`方法在处理数组形式参数时的执行机制
Promise.all()` 提供了一种方便的方式来同时处理多个异步操作,并在它们都完成后获取到所有的结果,使得我们能够更高效地进行异步任务的组合和处理。
|
28天前
|
前端开发
`Promise.allSettled()`方法与`Promise.all()`方法有何不同?
`Promise.allSettled()` 提供了一种更灵活和全面的方式来处理多个 `Promise`,使得我们能够更好地应对各种异步操作的情况,尤其是需要详细了解每个 `Promise` 结果的场景。
|
23天前
|
监控 调度
在什么情况下应该使用 Promise.allSettled() 方法?
总的来说,`Promise.allSettled()` 为我们处理多个异步任务提供了一种更灵活、更全面的方式,使我们能够更好地应对各种复杂的情况,确保在获取到所有任务结果的同时,能够进行更有效的后续处理和决策。
|
28天前
|
前端开发 索引
Promise.all() 方法的参数可以是哪些数据类型?
`Promise.all()` 方法的参数具有很大的灵活性,可以适应多种不同的场景和需求,方便地处理多个异步操作的并发执行和结果汇总。
|
1月前
|
前端开发
Promise.race() 方法在什么场景下使用?
`Promise.race()` 方法通过其独特的竞争机制,在需要快速获取结果、设置超时控制、实现快速失败以及根据条件动态选择异步操作等场景中,能够提供简洁有效的解决方案,帮助优化异步操作的执行流程和提高系统的响应性能。
|
1月前
|
存储 Java 程序员
Java基础的灵魂——Object类方法详解(社招面试不踩坑)
本文介绍了Java中`Object`类的几个重要方法,包括`toString`、`equals`、`hashCode`、`finalize`、`clone`、`getClass`、`notify`和`wait`。这些方法是面试中的常考点,掌握它们有助于理解Java对象的行为和实现多线程编程。作者通过具体示例和应用场景,详细解析了每个方法的作用和重写技巧,帮助读者更好地应对面试和技术开发。
117 4
|
3月前
|
ARouter 测试技术 API
Android经典面试题之组件化原理、优缺点、实现方法?
本文介绍了组件化在Android开发中的应用,详细阐述了其原理、优缺点及实现方式,包括模块化、接口编程、依赖注入、路由机制等内容,并提供了具体代码示例。
51 2
下一篇
DataWorks