【面试题】吃透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、前端面试题库 (面试必备)            推荐:★★★★★

地址:前端面试题库

相关文章
|
3天前
|
前端开发 索引
Promise.all() 方法的参数可以是什么类型?
综上所述,`Promise.all()` 方法的参数类型较为灵活,但无论使用哪种类型的可迭代对象作为参数,其核心的异步操作处理逻辑和成功失败的判断机制都是一致的,都是为了方便地处理多个异步操作的并发执行和结果汇总。
|
6天前
|
存储 前端开发
除了 Promise.all(),还有哪些方法可以处理异步并发操作?
在上述示例中,`concurrentPromises` 函数接受一个Promise数组和最大并发数作为参数,通过手动控制并发执行的Promise数量,实现了对异步操作的并发控制,并在所有Promise完成后返回结果数组。
|
3天前
|
前端开发
Promise.race() 方法在什么场景下使用?
`Promise.race()` 方法通过其独特的竞争机制,在需要快速获取结果、设置超时控制、实现快速失败以及根据条件动态选择异步操作等场景中,能够提供简洁有效的解决方案,帮助优化异步操作的执行流程和提高系统的响应性能。
|
10天前
|
存储 Java 程序员
Java基础的灵魂——Object类方法详解(社招面试不踩坑)
本文介绍了Java中`Object`类的几个重要方法,包括`toString`、`equals`、`hashCode`、`finalize`、`clone`、`getClass`、`notify`和`wait`。这些方法是面试中的常考点,掌握它们有助于理解Java对象的行为和实现多线程编程。作者通过具体示例和应用场景,详细解析了每个方法的作用和重写技巧,帮助读者更好地应对面试和技术开发。
50 4
|
2月前
|
ARouter 测试技术 API
Android经典面试题之组件化原理、优缺点、实现方法?
本文介绍了组件化在Android开发中的应用,详细阐述了其原理、优缺点及实现方式,包括模块化、接口编程、依赖注入、路由机制等内容,并提供了具体代码示例。
45 2
|
3月前
|
Java
【Java基础面试二十】、介绍一下Object类中的方法
这篇文章介绍了Java中Object类的常用方法,包括`getClass()`、`equals()`、`hashCode()`、`toString()`、`wait()`、`notify()`、`notifyAll()`和`clone()`,并提到了不推荐使用的`finalize()`方法。
【Java基础面试二十】、介绍一下Object类中的方法
|
3月前
|
Java API 索引
【Java基础面试二十四】、String类有哪些方法?
这篇文章列举了Java中String类的常用方法,如`charAt()`、`substring()`、`split()`、`trim()`、`indexOf()`、`lastIndexOf()`、`startsWith()`、`endsWith()`、`toUpperCase()`、`toLowerCase()`、`replaceFirst()`和`replaceAll()`,并建议面试时展示对这些方法的熟悉度,同时深入理解部分方法的源码实现。
【Java基础面试二十四】、String类有哪些方法?
|
3月前
|
Java
【Java集合类面试三十】、BlockingQueue中有哪些方法,为什么这样设计?
BlockingQueue设计了四组不同行为方式的方法用于插入、移除和检查元素,以适应不同的业务场景,包括抛异常、返回特定值、阻塞等待和超时等待,以实现高效的线程间通信。
|
3月前
|
SQL 安全 测试技术
[go 面试] 接口测试的方法与技巧
[go 面试] 接口测试的方法与技巧
【多线程面试题 二】、 说说Thread类的常用方法
Thread类的常用方法包括构造方法(如Thread()、Thread(Runnable target)等)、静态方法(如currentThread()、sleep(long millis)、yield()等)和实例方法(如getId()、getName()、interrupt()、join()等),用于线程的创建、控制和管理。

热门文章

最新文章