[手工实现]1. Promise 详解剖析

实现目标

  1. 基础功能——then函数异步回调(不带链式调用功能)
  2. 基础功能——链式调用功能
  3. 基础功能——catch、finally函数功能
  4. 高级功能——Promise.resolve、Promise.reject
  5. 高级功能——Promise.all、Promise.race、Promise.allSettled
  6. 完整代码示例,并执行case与标准Promise比较

Promise的基本说明

Promise 对象是一个代理对象(代理一个值),被代理的值在Promise对象创建时可能是未知的。它允许你为异步操作的成功和失败分别绑定相应的处理方法(handlers)。 这让异步方法可以像同步方法那样返回值,但并不是立即返回最终执行结果,而是一个能代表未来出现的结果的promise对象

一个 Promise有以下几种状态:

  • pending: 初始状态,既不是成功,也不是失败状态。
  • fulfilled: 意味着操作成功完成。
  • rejected: 意味着操作失败。

pending 状态的 Promise 对象可能会变为fulfilled 状态并传递一个值给相应的状态处理方法,也可能变为失败状态(rejected)并传递失败信息。当其中任一种情况出现时,Promise 对象的 then 方法绑定的处理方法(handlers )就会被调用(then方法包含两个参数:onfulfilled 和 onrejected,它们都是 Function 类型。当Promise状态为fulfilled时,调用 then 的 onfulfilled 方法,当Promise状态为rejected时,调用 then 的 onrejected 方法, 所以在异步操作的完成和绑定处理方法之间不存在竞争)。

image.png

基础功能

Promise基本构造

/**
*@Promise的三种状态枚举
*/
enum PromiseState {
  pending,
  fulfilled,
  rejected
}
// Promise构造函数传入的执行器类型
type PromiseExecutor<T, E> = (resolve: PromiseResolve<T>, reject: PromiseReject<E>) => unknown;
type PromiseResolve<T> = (value?: T) => void;
type PromiseReject<T> = (error?: T) => void;
//Promise then函数onFulfilled参数类型
type PromiseThenOnFulfilled<T> = (value: T) => Promise | unknown;
//Promise then函数onRejected参数类型
type PromiseThenOnRejected<T> = (error: T) => Promise | unknown;
//Promise catch函数onExcepted参数类型
type PromiseCatchOnExcepted<T> = (error: T) => Promise | unknown;
type PromiseOnFinal = () => unknown;

export class Promise<T = unknown, E = unknown> {
  
  public readonly name = Symbol('Promise');
  // 标识当前实例的状态,初始化为pending
  private state: PromiseState = PromiseState.pending;
  // 成功后数据的存储
  private value!: T;
  // 失败或异常后数据的存储
  private error!: E;
  // then成功回调参数的存储
  private onFulfilled?: PromiseThenOnFulfilled<T>;
  // then失败回调参数的存储
  private onRejected?: PromiseThenOnRejected<E>;
  // catch函数参数的存储
  private onExcepted?: PromiseCatchOnExcepted<E>;
  // finally函数参数的存储
  private onFinal?: PromiseOnFinal;
  // nextPromise, 表明当then\catch\finally函数被调用后,返回的一个新的Promise实例。实现链式调用
  private nextPromise?: Promise;
  // 构造函数
  constructor(executor: PromiseExecutor<T, E> = () => { }) {

  }
  // then函数,当实例状态变更为fulfilled时,第一个函数参数被回调,当状态变更为rejected时,第二个函数参数被回调。如果一个参数都没有传入,状态会被派生至下次回调函数的出现。
  public then(onFulfilled?: PromiseThenOnFulfilled<T>, onRejected?: PromiseThenOnRejected<E>) {

  }
  // catch函数,当实例状态变更为rejected并且then函数没有传入第二个参数时,此函数中传入的参数将被回调。
  public catch(onExcepted?: PromiseCatchOnExcepted<E>) {

  }
  // finally函数,当实例状态变更为成功后失败后,该函数中传入的回调参数都会被调用。
  public finally(onFinal?: PromiseOnFinal) {

  }

}

then函数异步回调

1.Promise函数被创建时,传入的回调函数即会被调用,所以有如下代码

//some code...
// 实例初始化时即调用
  constructor(executor: PromiseExecutor<T, E> = () => { }) {
    executor(this.resolve, this.reject)
  }
// resolve被调用时,将值保存到实例中,并且更改状态为fulfilled
  private resolve = (value?: T) => {
    this.value = value as T;
    this.setState(PromiseState.fulfilled);
  }
// reject被调用时,将值保存到实例中,并且更改状态为rejected
  private reject = (error?: E) => {
    this.error = error as E;
    this.setState(PromiseState.rejected);
  }
// 统一状态设置入口,以便状态被改变时做一些事情
private setState = (state: PromiseState) => {
      this.state = state;
  }
//some code...
  1. then函数被调用时,将传入的函数保存到实例中,以便状态变更后回调
public then(onFulfilled?: PromiseThenOnFulfilled<T>, onRejected?: PromiseThenOnRejected<E>) {
    this.onFulfilled = onFulfilled;
    this.onRejected = onRejected;
  }
  1. 当状态被变更,触发传入的回调函数,修改之前的setState函数(暂不处理then函数传不传参的情况)
private setState = (state: PromiseState) => {
    this.state = state;
    if (state === PromiseState.fulfilled) {
        this.onFulfilled(this.value);
    }
    else if (state === PromiseState.rejected) {
        this.onRejected(this.error);
    }
}

链式回调功能实现

要实现链式回调功能,方法函数必须返回一个同类型的实例。并且,由于Promise的特性,下一个then等函数中回调函数的调用时机,也要根据当前实例then等函数返回的状态而决定。所以才有了第一段代码中nextPromise属性存在的意义。当then等函数被调用时,返回准备好的下一个新的Promise实例,此实例的状态是初始状态,当当前实例的onFulfilled/onRejected/onExcepted被调用后,可以知道其返回值是否为Promise,从而判断该如何将当前状态托管到下一个Promise。

  1. 改造then函数,使其返回一个新的Promise实例
public then(onFulfilled?: PromiseThenOnFulfilled<T>, onRejected?: PromiseThenOnRejected<E>) {
    this.onFulfilled = onFulfilled;
    this.onRejected = onRejected;
  // 返回一个新的Promise实例,下一个then或其他方法的调用,将会在此实例上进行
    return this.next();
  }

private next() {
  // 返回前检查下nextPromise是否被有值
    this.checkNextPromise();
    return this.nextPromise as Promise;
  }

private checkNextPromise(np: Promise | undefined = undefined): np is Promise {
  // 一旦发现nextPromise还没有值,就给他赋一个新的Promise实例上去
    if (!this.nextPromise) {
      this.nextPromise = new Promise(); // 没有传入任何参数的Promise的状态是会处于pending中的
    }
    return true;
  }
  1. 改造setState,在状态被设置的时候,调用相应的回调函数,并且根据回调函数的返回值类型来设置nextPromise
private resolve = (value?: T) => {
    this.value = value as T;
  // 将调用setState函数的时机改为异步调用
    setTimeout(this.setState, 0, PromiseState.fulfilled);
  }

  private reject = (error?: E) => {
    this.error = error as E;
  // 将调用setState函数的时机改为异步调用
    setTimeout(this.setState, 0, PromiseState.rejected);
  }

private setState = (state: PromiseState) => {
    const { nextPromise } = this;
    // 理论上是不会存在nextPromise没有值的,因为setState函数的调用一定在nextPromise被初始化之后
    if (!nextPromise) {
      return;
    }

    this.state = state;

    if (state === PromiseState.fulfilled) {
        this.handleFulfilled(nextPromise);
      }
  }

private handleFulfilled(next: Promise) {
    if (this.onFulfilled) {
// 如果有onFulfilled回调,则直接回调其函数,并且检查返回值是否是Promise
      this.checkCallResult(this.onFulfilled(this.value), next);
    }
    else {
// 如果没有,则将成功状态托管给下一个Promise,等待数据被取用
      next.resolve(this.value);
    }
  }

private checkCallResult(result: Promise | unknown, next: Promise) {
    if (result instanceof Promise) {
  // 如果返回值是Promise实例,则将生成的新Promise代理到返回的Promise上
      Promise.transmit(result, next);
    }
    else {
      next.resolve();
    }
  }

/** @static */
  public static transmit(from: Promise, to: Promise) {
    from.resolve = to.resolve;
    from.reject = to.reject;
  }

完善基础功能,实现catch、finally函数

catch函数的回调是在当实例状态变更为失败的时候并且then函数没有传入第二个值时才会被调用。调用后同then函数一样,链式传递。
finally函数的回调是在当实例状态非pending时,在最后始终会调用的一个回调。调用后同then函数一样,链式传递。

constructor(executor: PromiseExecutor<T, E> = () => { }) {
// Promise标准中,executor如果发生异常,也算作是rejected,所以需要捕获异常来达到目的
  try {
      executor(this.resolve, this.reject);
    } catch (error) {
      this.reject(error);
    }
  }

public catch(onExcepted?: PromiseCatchOnExcepted<E>) {
    this.onExcepted = onExcepted;
    return this.next();
  }

  public finally(onFinal?: PromiseOnFinal) {
    this.onFinal = onFinal;
    return this.next();
  }

private setState = (state: PromiseState) => {
    const { nextPromise } = this;

    if (!nextPromise) {
      return;
    }

    this.state = state;

// 不论是reject被调用还是直接发生throw,失败的回调函数都会被调用
    try {
      if (state === PromiseState.fulfilled) {
        this.handleFulfilled(nextPromise);
      }
      else if (state === PromiseState.rejected) {
        this.handleError(nextPromise);
      }
    } catch (error) {
      this.error = error;
      this.handleError(nextPromise);
    }
  }

private handleError(next: Promise) {
    if (this.onRejected) {
      this.checkCallResult(this.onRejected(this.error), next);
    }
    else if (this.onExcepted) {
      this.checkCallResult(this.onExcepted(this.error), next);
    }
    else if (this.onFinal) {
      this.onFinal();
      next.reject(this.error);
    }
    else {
      next.reject(this.error);
    }
  }

resolve和reject功能的实现

public static resolve<V>(value?: V | Promise) {
// 传入参数如果是Promise,则直接返回,如果是其他,则返回一个新的Promise,并且回调实例的resolve
    return value instanceof Promise ? value : new Promise((r) => r(value))
  }

  public static reject<R>(reason?: R) {
    return new Promise((r, j) => {
      j(reason)
    })
  }

Promise.all、Promise.race、Promise.allSettled功能实现

public static allSettled<R, E = R>(iterable: Promise<R, E>[]) {
    let complates: Array<R | E> = [];
    let completedNumber = 0;
    const result = new Promise();
    iterable.forEach((p, i) => {
      p.then(d => {
        complates[i] = d;
        completedNumber++;
        if (completedNumber === iterable.length) {
          result.resolve(complates);
        }
      }, e => {
        complates[i] = e;
        completedNumber++;
        if (completedNumber === iterable.length) {
          result.resolve(complates);
        }
      });
    })

    return result as Promise<Array<R | E>, E>;
  }

  public static all<R, E>(iterable: Promise<R, E>[] | Promise<R, E> | string) {
    let succs: R[] = [];
    let completedNumber = 0;
    const result = new Promise();
    const resolve = () => result.resolve(succs);
    const reject = (reason: E) => result.reject(reason);

    if (iterable instanceof Promise) {
      iterable.then(d => {
        succs.push(d)
        resolve();
      }, e => reject(e));
    }
    else if (typeof iterable === 'string') {
      succs = iterable.split('') as any[];
      setTimeout(() => resolve());
    }
    else if (iterable instanceof Array) {
      iterable.forEach((p, i) => {
        p.then(d => {
          completedNumber++;
          succs[i] = d;
          if (completedNumber === iterable.length) {
            resolve();
          }
        }, e => reject(e));
      })
    }

    return result as Promise<R[], E>;
  }

  public static race<T, E>(iterable: Promise[]) {
    const result = new Promise();

    iterable.forEach(item => {
      item.then(d => result.state === PromiseState.pending && result.resolve(d), e => result.state === PromiseState.pending && result.reject(e))
    })

    return result as Promise<T, E>;
  }

完整代码

enum PromiseState {
  pending,
  fulfilled,
  rejected
}
type PromiseExecutor<T, E> = (resolve: PromiseResolve<T>, reject: PromiseReject<E>) => unknown;
type PromiseResolve<T> = (value?: T) => void;
type PromiseReject<T> = (error?: T) => void;
type PromiseThenOnFulfilled<T> = (value: T) => Promise | unknown;
type PromiseThenOnRejected<T> = (error: T) => Promise | unknown;
type PromiseCatchOnExcepted<T> = (error: T) => Promise | unknown;
type PromiseOnFinal = () => unknown;

export class Promise<T = unknown, E = unknown> {

  public readonly name = Symbol('Promise');
  private state: PromiseState = PromiseState.pending;
  private value!: T;
  private error!: E;
  private onFulfilled?: PromiseThenOnFulfilled<T>;
  private onRejected?: PromiseThenOnRejected<E>;
  private onExcepted?: PromiseCatchOnExcepted<E>;
  private onFinal?: PromiseOnFinal;
  private nextPromise?: Promise;

  constructor(executor: PromiseExecutor<T, E> = () => { }) {
    this.handleExecutor(executor);
  }

  private handleExecutor(executor: PromiseExecutor<T, E>) {
    try {
      executor(this.resolve, this.reject);
    } catch (error) {
      this.reject(error);
    }
  }

  private handleFulfilled(next: Promise) {
    if (this.onFulfilled) {
      this.checkCallResult(this.onFulfilled(this.value), next);
    }
    else if (this.onFinal) {
      this.onFinal();
      next.resolve(this.value);
    }
    else {
      next.resolve(this.value);
    }
  }

  private handleError(next: Promise) {
    if (this.onRejected) {
      this.checkCallResult(this.onRejected(this.error), next);
    }
    else if (this.onExcepted) {
      this.checkCallResult(this.onExcepted(this.error), next);
    }
    else if (this.onFinal) {
      this.onFinal();
      next.reject(this.error);
    }
    else {
      next.reject(this.error);
    }
  }

  private setState = (state: PromiseState) => {
    const { nextPromise } = this;

    if (!nextPromise) {
      return;
    }

    this.state = state;

    try {
      if (state === PromiseState.fulfilled) {
        this.handleFulfilled(nextPromise);
      }
      else if (state === PromiseState.rejected) {
        this.handleError(nextPromise);
      }
    } catch (error) {
      this.error = error;
      this.handleError(nextPromise);
    }
  }

  private checkCallResult(result: Promise | unknown, next: Promise) {
    if (result instanceof Promise) {
      Promise.transmit(result, next);
    }
    else {
      next.resolve();
    }
  }

  private resolve = (value?: T) => {
    this.value = value as T;
    setTimeout(this.setState, 0, PromiseState.fulfilled);
  }

  private reject = (error?: E) => {
    this.error = error as E;
    setTimeout(this.setState, 0, PromiseState.rejected);
  }

  private next() {
    this.checkNextPromise();
    return this.nextPromise as Promise;
  }

  private checkNextPromise(np: Promise | undefined = undefined): np is Promise {
    if (!this.nextPromise) {
      this.nextPromise = new Promise(() => { });
    }
    return true;
  }

  public then(onFulfilled?: PromiseThenOnFulfilled<T>, onRejected?: PromiseThenOnRejected<E>) {
    this.onFulfilled = onFulfilled;
    this.onRejected = onRejected;
    return this.next();
  }

  public catch(onExcepted?: PromiseCatchOnExcepted<E>) {
    this.onExcepted = onExcepted;
    return this.next();
  }

  public finally(onFinal?: PromiseOnFinal) {
    this.onFinal = onFinal;
    return this.next();
  }

  /** @static */
  public static transmit(from: Promise, to: Promise) {
    from.resolve = to.resolve;
    from.reject = to.reject;
  }

  public static resolve<V>(value?: V | Promise) {
    return value instanceof Promise ? value : new Promise((r) => r(value))
  }

  public static reject<R>(reason?: R) {
    return new Promise((r, j) => {
      j(reason)
    })
  }

  public static allSettled<R, E = R>(iterable: Promise<R, E>[]) {
    let complates: Array<R | E> = [];
    let completedNumber = 0;
    const result = new Promise();
    iterable.forEach((p, i) => {
      p.then(d => {
        complates[i] = d;
        completedNumber++;
        if (completedNumber === iterable.length) {
          result.resolve(complates);
        }
      }, e => {
        complates[i] = e;
        completedNumber++;
        if (completedNumber === iterable.length) {
          result.resolve(complates);
        }
      });
    })

    return result as Promise<Array<R | E>, E>;
  }

  public static all<R, E>(iterable: Promise<R, E>[] | Promise<R, E> | string) {
    let succs: R[] = [];
    let completedNumber = 0;
    const result = new Promise();
    const resolve = () => result.resolve(succs);
    const reject = (reason: E) => result.reject(reason);

    if (iterable instanceof Promise) {
      iterable.then(d => {
        succs.push(d)
        resolve();
      }, e => reject(e));
    }
    else if (typeof iterable === 'string') {
      succs = iterable.split('') as any[];
      setTimeout(() => resolve());
    }
    else if (iterable instanceof Array) {
      iterable.forEach((p, i) => {
        p.then(d => {
          completedNumber++;
          succs[i] = d;
          if (completedNumber === iterable.length) {
            resolve();
          }
        }, e => reject(e));
      })
    }

    return result as Promise<R[], E>;
  }

  public static race<T, E>(iterable: Promise[]) {
    const result = new Promise();

    iterable.forEach(item => {
      item.then(d => result.state === PromiseState.pending && result.resolve(d), e => result.state === PromiseState.pending && result.reject(e))
    })

    return result as Promise<T, E>;
  }

}

/** case */

// new Promise((resolve, reject) => {
//   console.log('start')
//   // resolve(1)
//   setTimeout(() => {
//     resolve(123)
//   }, 1000)
// }).then((data) => {
//   console.log(data)
//   return new Promise((r) => setTimeout(() => r(2), 2000))
// }).then((data) => {
//   console.log(data)
// }).then(() => {
//   console.log(22222)
//   return new Promise((r) => r('eeeeee'))
// }).then().then((data) => {
//   console.log(data)
//   console.log('end')
// })

// new Promise((resolve, reject) => {
//   setTimeout(() => resolve(1), 1000);
//   // reject(1)
// }).then((data) => {
//   console.log(data)
//   return new Promise((r) => {
//     setTimeout(() => r(1.1), 1000)
//   })
// }).finally(() => {
//   console.log('finally')
//   console.log(123)
// }).then((data) => {
//   console.log('then')
//   console.log(data);
// }).catch((e) => {
//   console.log('catch')
//   console.log(e);
// }).then((data) => {
//   console.log('then: last')
//   console.log(data)
// })

// new Promise((resolve, reject) => {
//   // setTimeout(() => resolve(1), 1000);
//   setTimeout(() => reject(2), 1000);
// }).then((data) => {
//   console.log(data)
// }).catch((e) => {
//   console.log(e)
//   return new Promise((r, j) => {
//     setTimeout(() => j('e.1'), 1000);
//   })
// }).finally(() => {
//   console.log('finally')
// }).then((data) => {
//   console.log('then')
//   console.log(data);
// }).catch((e) => {
//   console.log('catch')
//   console.log(e);
// }).catch((e) => {
//   console.log(e)
// }).then((data) => {
//   console.log(data)
// })

// new Promise((resolve, reject) => {
//   setTimeout(() => {
//     if (Date.now() % 2 === 0) {
//       resolve(1);
//     }
//     else {
//       reject(2);
//     }
//   }, 1000)
// }).then((data) => {
//   console.log(data)
// }, (e) => {
//   console.log(e);
//   console.log('111')
//   return new Promise((r) => {
//     r('success')
//   })
// }).catch((e) => {
//   console.log('222')
//   console.log(e);
// }).then((data) => {
//   console.log(data)
// })

// new Promise((resolve, reject) => {
//   throw 'Promise Error!'
//   // reject(1)
//   resolve(1)
// }).then((data) => {
//   console.log(data)
//   throw 'PPPP'
// }).then((data) => {
//   console.log(data)
// }).catch((e) => {
//   console.log(e)
// })

// Promise.resolve(Promise.resolve(11)).then((data) => {
//   console.log(data)
// }, e => console.log(e))

// async function test() {
//   const a = await new Promise<number>((r) => {
//     console.log(0)
//     setTimeout(() => r(1), 1000)
//   })

//   console.log(a)
//   return a;
// }
// test();

// Promise.all([Promise.resolve(1), Promise.resolve(2), Promise.reject(3)]).then(d => console.log(d)).catch(e => console.log(e))

// Promise.reject(1).finally().finally().then().catch().then((d) => {
//   console.log('then', d);
// }).catch((e) => {
//   console.log(e)
// }).then((d) => {
//   console.log('then', d);
// }).catch((e) => {
//   console.log(e)
// }).finally(() => {
//   console.log('The end')
// })

// Promise.race([new Promise(r => setTimeout(() => r(1), 1000)), new Promise((r, j) => setTimeout(() => j(2), 500)), new Promise(r => setTimeout(() => r(3), 1000))]).then(console.log, console.log)

// Promise.all([new Promise(r => setTimeout(() => r(1), 1000)), new Promise((r, j) => setTimeout(() => r(2), 500)), new Promise(r => setTimeout(() => r(3), 1000))]).then(console.log, console.log)

// Promise.allSettled([new Promise(r => setTimeout(() => r(1), 1000)), new Promise((r, j) => setTimeout(() => j(2), 500)), new Promise(r => setTimeout(() => r(3), 1000))]).then(console.log, console.log)

// const np = new Promise((r, j) => {
//   // setTimeout(r, 0, 1)
//   r(1)
//   console.log(111)
// })
// console.log(0)
// np.then(d => {
//   console.log(d)
// });

// new Promise(() => {
//   setTimeout(() => {
//     throw 1
//   }, 1000)
// }).catch(console.log)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。