前端面试题 - 55. Promise finally 怎么实现的(附Promise的完整实现源码)

lxf2023-05-05 06:13:01

本文包含以下内容:

  1. 简单Promise的实现
  2. 链式调用
  3. Promise.all
  4. Promise.resolve
  5. Promise finally
  6. Promise catch

1. 简单Promise的实现

只需要实现下面几个常用部分即可

    1. 构造函数 - 可以传入传入回调函数。并提供resolve/reject函数更新内部状态
    1. then - 注册异步成功和失败回调函数,如果状态修改了就立即执行
    1. resolve/reject - 内部更新状态,并执行注册的异步成功和失败回调函数
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

/**
 * 1. 构造函数 - 可以传入传入回调函数。并提供resolve/reject函数更新内部状态
 * 2. then - 注册异步成功和失败回调函数,如果状态修改了就立即执行
 * 3. resolve/reject - 内部更新状态,并执行注册的异步成功和失败回调函数
 */
class Promise2 {
  /** 当前状态 */
  private status: string = PENDING;
  /** 成功值 */
  private value: any = null;
  /** 失败原因 */
  private reason: any = null;

  /** 异步方法缓存,当状态改变后执行 */
  private FULFILLED_CALLBACK: Function | null = null;
  private REJECTED_CALLBACK: Function | null = null;;

  constructor(callback: Function) {
    callback(this.resolve, this.reject)
  }

  public resolve = (value: any) => {
    if (this.status !== PENDING) {
      return
    }
    this.status = FULFILLED
    this.value = value
    // 异步执行
    this.FULFILLED_CALLBACK?.(this.value);
  }

  public reject = (reason: any) => {
    if (this.status !== PENDING) {
      return
    }
    this.status = REJECTED
    this.reason = reason
    // 异步执行
    this.REJECTED_CALLBACK?.(this.reason);
  }

  public then(successCallback: Function, errorCallback: Function) {
    this.FULFILLED_CALLBACK = successCallback;
    this.REJECTED_CALLBACK = errorCallback;

    // 如果已经结束则立即执行
    if (this.status === FULFILLED) {
      successCallback(this.value)
    } else if (this.status === REJECTED) {
      errorCallback(this.reason)
    }
  }
}

export default Promise2;

测试:

const promise = new Promise2((resolve: Function, reject: Function) => {
  setTimeout(() => {
    resolve('new promise')
  }, 1000)
  // reject('reaspn')
})

promise.then((res: any) => {
  console.log(res); // new promise
}, (reason: any) => {
  console.log(reason);
})

前端面试题 - 55. Promise finally 怎么实现的(附Promise的完整实现源码)

2. 链式调用

不管 new Promise 创建出来的执行状态是成功 / 失败,只要在 then / catch方法中通过 return 返回一个结果,不管这个值是 Promise 对象还是普通值,都可以通过链式调用的 .then 方法中获取到这个值,因为 promise.then 方法会默认在返回值的外层包裹一层 Promise 对象,这样才可以实现 Promise 一直通过 .then 的方式去链式调用。

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

/**
 * 判断执行完的函数返回结果是否为 Promise 对象
 * 1. Promise 对象 - .then方法传入resolve, reject进行返回
 * 2. 普通值 - resolve返回
*/
function isPromise2(result: any, resolve: Function, reject: Function) {
  if (result instanceof Promise2) {
    result.then(resolve, reject)
  } else {
    resolve(result)
  }
}

/**
 * // 有一点没看懂的就是this ??
 */
class Promise2 {
  /** 当前状态 */
  private status: string = PENDING;
  /** 成功值 */
  private value: any = null;
  /** 失败原因 */
  private reason: any = null;

  /** 异步方法缓存,当状态改变后执行 */
  private fulfilledList: Function[] = []
  private rejectedList: Function[] = []

  constructor(callback: Function) {
    callback(this.resolve, this.reject)
  }

  public resolve = (value: any) => {
    if (this.status !== PENDING) {
      return
    }
    this.status = FULFILLED
    this.value = value
    // 多个then注册的方法依次执行掉
    while (this.fulfilledList.length) {
      this.fulfilledList.shift()?.()
    }
  }

  public reject = (reason: any) => {
    if (this.status !== PENDING) {
      return
    }
    this.status = REJECTED
    this.reason = reason
    // 多个then注册的方法依次执行掉
    while (this.rejectedList.length) {
      this.rejectedList.shift()?.()
    }
  }

  // then返回一个新的Promise
  public then(successCallback: Function, errorCallback?: Function) {
    const _this = this;
    // ? 为何这里new Promise2会带有之前的this信息
    return new Promise2((resolve: Function, reject: Function) => {
      // ? 这里的this为何不是新对象的this而是外面的this
      console.log(_this === this)// true

      if (this.status === FULFILLED) {
        // 增加一个 setTimeout 的原因是把当前任务放到下一个宏任务里去执行
        // 因为处理异步可能会有问题
        setTimeout(() => {
          try {
            let result = successCallback(this.value)
            isPromise2(result, resolve, reject)
          } catch (error) {
            reject(error)
          }
        })
      } else if (this.status === REJECTED) {
        setTimeout(() => {
          try {
            let result = errorCallback?.(this.value)
            isPromise2(result, resolve, reject)
          } catch (error) {
            reject(error)
          }
        })
      } else {
        this.fulfilledList.push(() => {
          setTimeout(() => {
            try {
              let result = successCallback(this.value)
              isPromise2(result, resolve, reject)
            } catch (error) {
              reject(error)
            }
          })
        })
        this.rejectedList.push(() => {
          setTimeout(() => {
            try {
              let result = errorCallback?.(this.value)
              isPromise2(result, resolve, reject)
            } catch (error) {
              reject(error)
            }
          })
        })
      }
    })
  }

}

export default Promise2;

测试

const promise = new Promise2((resolve: Function, reject: Function) => {
  resolve('new promise111111')
})

promise
.then(
  (res: any) => {
    console.log(res); // 返回值 new promise111111
    return '链式调用的方式'
  }, (reason: any) => { 
    console.log(reason);
  }
)
.then((value: any) => {
  console.log(value); // 返回值 链式调用的方式
})

前端面试题 - 55. Promise finally 怎么实现的(附Promise的完整实现源码)

3. Promise.all

多个成功或一个失败结果的收集,放一个Promise中处理。

 // 在一个Promise中处理。如果完成就addData否则reject
  static all(arr: Promise2[]) {
    // 创建一个保存所有结果的list
    let resultList: any[] = []
    const len = arr.length
    let index = 0
    // 返回一个 Promise 对象,让外部可以调用 then 方法获取结果
    return new Promise2((resolve: Function, reject: Function) => {
      // 保存结果
      function addData(key: any, value: any) {
        resultList[key] = value
        index++
        if (index === len) {
          resolve(resultList)
        }
      }
      // 循环执行传入的回调函数获取返回结果
      for (let i = 0; i < len; i++) {
        // 判断当前项,是否为一个 Promise 对象, 如果是, 调用它的then方法获取返回结果
        if (arr[i] instanceof Promise2) {
          arr[i].then((value: any) => addData(i, value), (reason: any) => reject(reason))
        } else {
          // 是普通对象的话, 直接返回结果
          addData(i, arr[i])
        }
      }
    })
  }

测试

// case3:Promise2.all
Promise2.all([1, promise, '100']).then((value: any) => {
  console.log(value);
}, (reason: any) => {
  console.log(reason);
})

前端面试题 - 55. Promise finally 怎么实现的(附Promise的完整实现源码)

4. Promise.resolve

  1. 在 resolve 判断当前传入参数是否为 Promsie 对象,是 Promise 的话, 直接返回这个参数
  2. 如果是普通值的话,创建一个 Promise 对象,调用 promise 的 resolve 参数返回传入的值
  static resolve(value: any) {
    if (value instanceof Promise2) return value
    return new Promise2((resolve: Function) => {
      resolve(value)
    })
  }

测试

Promise2.resolve('test').then((value: any) => {
  console.log(value);
});

前端面试题 - 55. Promise finally 怎么实现的(附Promise的完整实现源码)

5. Promise.finally

  1. 调用当前 Promise 的 then 方法返回一个新的 Promise 对象(保证链式调用)
  2. 调用 Promise 中的 resolve 方法进行返回
  // 1. 调用当前 Promise 的 then 方法返回一个新的 Promise 对象(保证链式调用)
  // 2. 调用 Promise 中的 resolve 方法进行返回
  public finally(callback: Function) {
    return this.then(
      (value: any) => this.resolve(callback()).then(() => value),
      (reason: any) =>
        this.resolve(callback()).then(() => {
          throw reason;
        })
    );
  }

测试

new Promise2((resolve, reject) => {
  resolve('成功')
  // reject('失败')
}).then(res => {
  console.log(res, 'resolve2');
}, reason => {
  console.log(reason, 'reject2');
}).finally(() => {
  console.log('执行了finally方法2');
})

前端面试题 - 55. Promise finally 怎么实现的(附Promise的完整实现源码)

6. Promise.catch

只执行reject就行了

  public catch(cb: Function) {
    this.then(undefined, cb)
  }

测试

new Promise2((resolve, reject) => {
  reject('失败')
}).catch(e => {
  console.log(e)
})

前端面试题 - 55. Promise finally 怎么实现的(附Promise的完整实现源码)

完整代码

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

/**
 * 判断执行完的函数返回结果是否为 Promise 对象
 * 1. Promise 对象 - .then方法传入resolve, reject进行返回
 * 2. 普通值 - resolve返回
*/
function isPromise2(result: any, resolve: Function, reject: Function) {
  if (result instanceof Promise2) {
    result.then(resolve, reject)
  } else {
    resolve(result)
  }
}

/**
 * 1. 构造函数 - 可以传入传入回调函数。并提供resolve/reject函数更新内部状态
 * 2. then - 注册异步成功和失败回调函数,如果状态修改了就立即执行
 * 3. resolve/reject - 内部更新状态,并执行注册的异步成功和失败回调函数
 */
class Promise2 {
  /** 当前状态 */
  private status: string = PENDING;
  /** 成功值 */
  private value: any = null;
  /** 失败原因 */
  private reason: any = null;

  /** 异步方法缓存,当状态改变后执行 */
  private fulfilledList: Function[] = []
  private rejectedList: Function[] = []

  constructor(callback: Function) {
    callback(this.resolve, this.reject)
  }

  public resolve = (value: any) => {
    if (this.status !== PENDING) {
      return
    }
    this.status = FULFILLED
    this.value = value
    // 多个then注册的方法依次执行掉
    while (this.fulfilledList.length) {
      this.fulfilledList.shift()?.()
    }
  }

  public reject = (reason: any) => {
    if (this.status !== PENDING) {
      return
    }
    this.status = REJECTED
    this.reason = reason
    // 多个then注册的方法依次执行掉
    while (this.rejectedList.length) {
      this.rejectedList.shift()?.()
    }
  }

  // 1. 调用当前 Promise 的 then 方法返回一个新的 Promise 对象(保证链式调用)
  // 2. 调用 Promise 中的 resolve 方法进行返回
  public finally(callback: Function) {
    return this.then(
      (value: any) => this.resolve(callback()).then(() => value),
      (reason: any) =>
        this.resolve(callback()).then(() => {
          throw reason;
        })
    );
  }

  // then返回一个新的Promise
  public then(successCallback?: Function, errorCallback?: Function) {
    const _this = this;
    // ? 为何这里new Promise2会带有之前的this信息
    return new Promise2((resolve: Function, reject: Function) => {
      // ? 这里的this为何不是新对象的this而是外面的this
      console.log(_this === this)// true

      if (this.status === FULFILLED) {
        // 增加一个 setTimeout 的原因是把当前任务放到下一个宏任务里去执行
        // 因为处理异步可能会有问题
        setTimeout(() => {
          try {
            let result = successCallback?.(this.value)
            isPromise2(result, resolve, reject)
          } catch (error) {
            reject(error)
          }
        })
      } else if (this.status === REJECTED) {
        setTimeout(() => {
          try {
            let result = errorCallback?.(this.reason)
            isPromise2(result, resolve, reject)
          } catch (error) {
            reject(error)
          }
        })
      } else {
        this.fulfilledList.push(() => {
          setTimeout(() => {
            try {
              let result = successCallback(this.value)
              isPromise2(result, resolve, reject)
            } catch (error) {
              reject(error)
            }
          })
        })
        this.rejectedList.push(() => {
          setTimeout(() => {
            try {
              let result = errorCallback?.(this.reason)
              isPromise2(result, resolve, reject)
            } catch (error) {
              reject(error)
            }
          })
        })
      }
    })
  }

  public catch(cb: Function) {
    this.then(undefined, cb)
  }

  // 在一个Promise中处理。如果完成就addData否则reject
  static all(arr: any[]) {
    // 创建一个保存所有结果的list
    let resultList: any[] = []
    const len = arr.length
    let index = 0
    // 返回一个 Promise 对象,让外部可以调用 then 方法获取结果
    return new Promise2((resolve: Function, reject: Function) => {
      // 保存结果
      function addData(key: any, value: any) {
        resultList[key] = value
        index++
        if (index === len) {
          resolve(resultList)
        }
      }
      // 循环执行传入的回调函数获取返回结果
      for (let i = 0; i < len; i++) {
        // 判断当前项,是否为一个 Promise 对象, 如果是, 调用它的then方法获取返回结果
        if (arr[i] instanceof Promise2) {
          arr[i].then((value: any) => addData(i, value), (reason: any) => reject(reason))
        } else {
          // 是普通对象的话, 直接返回结果
          addData(i, arr[i])
        }
      }
    })
  }

  // 对普通值和Promise分别处理
  static resolve(value: any) {
    if (value instanceof Promise2) return value
    return new Promise2((resolve: Function) => {
      resolve(value)
    })
  }
}

本文主要参考 Promise的实现原理系列