乐趣区

关于javascript:Promise的实现和面试常考点

Promise

1、Promise 是一个类,类中须要传入一个 executor 执行器,默认会 立刻执行
2、Promise 有三种状态,pending , resolved , rejected , 只能从期待态转为其余两种状态
3、promise 官网标准:https://promisesaplus.com/
4、promise 反对链式调用,外部原理采纳的是 公布订阅模式
5、每次调用 then 办法,都会返回一个新的 promise
6、.then 中什么时候走失败:返回的是一个失败的 promise,抛出异样也会走失败,其余都走胜利
7、catch 用来对立捕捉谬误,它的特点是:如果一层层往下找,都没有处理错误的函数,则会找到最近的 catch,catch 也是 then, 遵循 then 的规定

第一版:没有任何异步逻辑

const PENDING = 'PENDING'
const RESOLVED = 'RESOLVED'
const REJECTED = 'REJECTED'
class Promise {constructor(executor) {
        this.status = PENDING;
        this.value = undefined
        this.reason = undefined
        let resolve = (value) => {if (this.status === PENDING) {
                this.value = value
                this.status = RESOLVED
            }
        }
        let reject = (reason) => {if (this.status === PENDING) {
                this.reason = reason
                this.status = REJECTED
            }
        }
        try {// 为什么加 try..catch,因为外部可能间接出错
            executor(resolve, reject)// 给用户提供两个函数
        } catch (e) {reject(e)
        }
    }
    then(onfulfilled, onrejected) {if (this.status === RESOLVED) {onfulfilled(this.value)
        }
        if (this.status === REJECTED) {onrejected(this.reason)
        }
    }
}
module.exports = Promise

第二版:如果 executor 外面有异步操作,则须要采纳公布订阅模式进行解决

const PENDING = 'PENDING'
const RESOLVED = 'RESOLVED'
const REJECTED = 'REJECTED'
class Promise {constructor(executor) {
        this.status = PENDING;
        this.value = undefined
        this.reason = undefined
        // 专门寄存胜利的回调的函数
        this.onResolvedCallbacks = [];
        // 专门寄存失败的回调函数的
        this.onRejectedCallbacks = [];
        let resolve = (value) => {if (this.status === PENDING) {
                this.value = value
                this.status = RESOLVED
                // 当调用 resolve 时,阐明要走胜利态,所以顺次执行 then 中胜利的回调
                this.onResolvedCallbacks.forEach(fn => fn())
            }
        }
        let reject = (reason) => {if (this.status === PENDING) {
                this.reason = reason
                this.status = REJECTED
                // 当调用 reject 时,阐明要走失败态,所以执行 then 中失败的回调
                this.onRejectedCallbacks.forEach(fn => fn())
            }
        }
        try {executor(resolve, reject)
        } catch (e) {reject(e)
        }
    }
    then(onfulfilled, onrejected) {if (this.status === RESOLVED) {onfulfilled(this.value)
        }
        if (this.status === REJECTED) {onrejected(this.reason)
        }
        // 如果有异步操作(如 setTimeout), 执行 then 办法时状态还是 PENDING,则须要将 then 中的两个办法先存起来
        if (this.status === PENDING) {
            // 第一种:将这个函数间接扔进去,然而不太好,不不便扩大本人的性能
            // this.onResolvedCallbacks.push(onfulfilled)
            // this.onRejectedCallbacks.push(onrejected)
            // 第二种:应用切片编程,外面包一层,不便扩大
            this.onResolvedCallbacks.push(() => {onfulfilled(this.value)
            })
            this.onRejectedCallbacks.push(() => {onrejected(this.reason)
            })
        }

    }


}
module.exports = Promise

第三版:then 的链式调用及 then 是个异步操作

const PENDING = 'PENDING'
const RESOLVED = 'RESOLVED'
const REJECTED = 'REJECTED'
// 个别微工作比宏工作早执行,也不是相对的,setTimeout 是宏工作
// 判断 x 的状态 是让 promise2 走胜利态还是失败态
function resolvePromise(promise2, x, resolve, reject) {// TODO}
class Promise {constructor(executor) {
        this.status = PENDING;
        this.value = undefined
        this.reason = undefined
        this.onResolvedCallbacks = [];
        this.onRejectedCallbacks = [];
        let resolve = (value) => {if (this.status === PENDING) {
                this.value = value
                this.status = RESOLVED
                this.onResolvedCallbacks.forEach(fn => fn())
            }
        }
        let reject = (reason) => {if (this.status === PENDING) {
                this.reason = reason
                this.status = REJECTED
                this.onRejectedCallbacks.forEach(fn => fn())
            }
        }
        try {executor(resolve, reject)
        } catch (e) {reject(e)
        }
    }
    then(onfulfilled, onrejected) {
        // 链式调用,返回的须要是个新的 promise
        const promise2 = new Promise((resolve, reject) => {
            // 将之前判断条件放到这个 promise 的外面,因为它是 promise,所以是立刻执行,不影响
            if (this.status === RESOLVED) {
                // 将 then 执行之后的后果传到下一个 promise 的 resolve 或者 reject 中
                // 须要判断这个 x 是一般值还是 promise,如果是 promise,则须要让这个 promise 执行,执行之后的状态将作为 promise2 的状态
                setTimeout(() => {// 为什么须要 setTimeout? 因为官网说了 then 是个异步操作
                    try {// 为什么要包住,因为执行 onfulfilled 可能会出错
                        let x = onfulfilled(this.value)
                        // 我须要依据 x,判断调用 promise2 的 resolve 还是 reject,所以将 promise2 和它的 resolve 和 reject 传过来
                        // 此时应该没有 promise2,应该怎么搞?答案就是通过增加宏工作(setTimeout)或者微工作(nextTick)
                        resolvePromise(promise2, x, resolve, reject)
                    } catch (e) {reject(e)
                    }

                }, 0)
            }
            if (this.status === REJECTED) {setTimeout(() => {
                    try {let x = onrejected(this.reason)
                        resolvePromise(x)

                    } catch (e) {reject(e)
                    }

                }, 0);
            }
            if (this.status === PENDING) {this.onResolvedCallbacks.push(() => {setTimeout(() => {
                        try {let x = onfulfilled(this.value)
                            resolvePromise(x)

                        } catch (e) {reject(e)
                        }

                    }, 0);
                })
                this.onRejectedCallbacks.push(() => {setTimeout(() => {
                        try {let x = onrejected(this.reason)
                            resolvePromise(x)

                        } catch (e) {reject(e)
                        }

                    }, 0);
                })
            }

        })

    }


}
module.exports = Promise

第四版:resolvePromise 办法的实现

const PENDING = 'PENDING'
const RESOLVED = 'RESOLVED'
const REJECTED = 'REJECTED'
function resolvePromise(promise2, x, resolve, reject) {
    // 此办法 为了兼容所有的 promise,n 个库两头 执行的流程是一样的
    // 尽可能具体 不出错
    // 1) 不能引用同一个对象 可能会造成死循环
    if (promise2 === x) {return reject(new TypeError('Chaining cycle detected for promise #<Promise> --'))
    }
    let called;
    // 2) 判断 x 的类型 x 是对象或者函数,才有可能是 promise
    if ((typeof x === 'object' && x != null) || typeof x === 'function') {
        try {let then = x.then;  // {a:1} 因为 then 办法 可能应用的 getter 来定义的
            if (typeof then === 'function') { // 只能认为他是 promise 了
                // call 扭转 this 指向 并且让函数执行
                then.call(x, y => { // 只取一次 以后 promise 解析进去的后果可能还是一个 promise 持续解析直到他是一个一般值为止
                    if (called) return;
                    called = true;
                    // 递归解析 resolve 的值
                    resolvePromise(promise2, y, resolve, reject)
                }, r => {if (called) return;
                    called = true;
                    reject(r);
                })
            } else {// 如果不是函数,则只能是这种对象了{a:1,then:1}
                resolve(x)
            }
        } catch (e) { //  我取 then 出错了 在谬误中又掉了该 promise 的胜利
            if (called) return
            called = true;
            reject(e); // 取值失败 就走到 error 中
        }
    } else {
        // 进到这里阐明是一般值,走胜利态
        resolve(x)
    }
}
class Promise {constructor(executor) {
        this.status = PENDING;
        this.value = undefined
        this.reason = undefined
        this.onResolvedCallbacks = [];
        this.onRejectedCallbacks = [];
        let resolve = (value) => {if (this.status === PENDING) {
                this.value = value
                this.status = RESOLVED
                this.onResolvedCallbacks.forEach(fn => fn())
            }
        }
        let reject = (reason) => {if (this.status === PENDING) {
                this.reason = reason
                this.status = REJECTED
                this.onRejectedCallbacks.forEach(fn => fn())
            }
        }
        try {executor(resolve, reject)
        } catch (e) {reject(e)
        }
    }
    catch(errCallback){ // catch 就是没有胜利的 then 办法
        return this.then(null,errCallback)
    }
    then(onfulfilled, onrejected) {const promise2 = new Promise((resolve, reject) => {if (this.status === RESOLVED) {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(x)

                    } catch (e) {reject(e)
                    }

                }, 0);
            }
            if (this.status === PENDING) {this.onResolvedCallbacks.push(() => {setTimeout(() => {
                        try {let x = onfulfilled(this.value)
                            resolvePromise(x)

                        } catch (e) {reject(e)
                        }

                    }, 0);
                })
                this.onRejectedCallbacks.push(() => {setTimeout(() => {
                        try {let x = onrejected(this.reason)
                            resolvePromise(x)

                        } catch (e) {reject(e)
                        }

                    }, 0);
                })
            }

        })

    }


}
module.exports = Promise

// npm install -g promises-aplus-tests

常见面试题

  • promise链式调用原理和 jquery 链式调用原理区别

答:jquery 中的链式调用原理是通过返回 this 实现的,而 promise 的链式调用是通过返回新的 promise 实现的
  • Promise.all办法的原理

答:all 办法的特点是让所有的 promise 并发执行,外部应用 ** 计数器 ** 的形式判断是否全副执行完了,外部应用递归
  • Promise.resolve 和 Promise.reject 区别
答:Promise.resolve()参数能够承受一个 promise,并且期待它状态扭转;Promise.reject()也能够承受 promise,然而不会有期待成果,间接会走失败态
退出移动版