方法 手写promise_实现一个符合 Promise/A+规范的 Promise(typescript 版)

本文介绍了如何使用typescript从零开始手写一个符合Promise/A+规范的Promise。文章深入探讨了Promise的基本功能、then方法的实现(包括链式调用和值的穿透),以及Promise的拓展方法如catch、resolve、reject、all、race等。通过代码实现和规范测试,展示了Promise的工作原理和重要性。
摘要由CSDN通过智能技术生成

(给前端大全加星标,提升前端技能)

转自:Col0ring

juejin.cn/post/6886360224308035598

写在前面

没错,这又是一篇关于手写 Promise 的文章,想必大家已经看过很多相关 Promise 的文章,关于一些 Promise 出现原因等问题我就不详细说了,网上有很多资料。这次我们使用 typescript,从 ES6 中的 Promise 类型定义入手,分析 Promise 及相关方法的传入参数和返回值,手写一个 typescript 版本的 Promise。

Promise/A+ 规范

Promise/A+ 规范是业内所有的 Promise 类库的统一规范,我们要写的 Promise 也要符合这一规范(英文文档请查看 Promises/A+:https://promisesaplus.com/,相关中文翻译 Promise A+ 规范:http://malcolmyu.github.io/malnote/2015/06/12/Promises-A-Plus/,感谢译者)。

由于内容过多,在下面的编码中会一一进行实现,这里先提出几个术语:

  • 解决(fulfill):指一个 promise 成功时进行的一系列操作,如状态的改变、回调的执行。虽然规范中用 fulfill 来表示解决,但在后世的 promise 实现多以 resolve 来指代之。
  • 拒绝(reject):指一个 promise 失败时进行的一系列操作。
  • 终值(eventual value):所谓终值,指的是 promise 被解决(fulfill)时传递给解决回调的值,由于 promise 有一次性的特征,因此当这个值被传递时,标志着 promise 等待态的结束,故称之终值,有时也直接简称为值(value)
  • 据因(reason):也就是拒绝原因,指在 promise 被拒绝(reject) 时传递给拒绝回调的值。

值得注意的是,核心的 Promises/A+ 规范不设计如何创建、解决和拒绝 promise,而是专注于提供一个通用的 then 方法。所以,完成了对于then方法的交互,其实也就基本完成了对于 Promises/A+ 规范的实现。

实现 Promise

基本功能

首先我们要实现 Promise 的基本功能,传入生成器,reslovereject函数的执行以及调用then函数对于值的基本获取,先来看看构造函数的类型定义:

// 这是类本身,还有 all, race 等方法在这里面定义
interface PromiseConstructor {
    /**
     * A reference to the prototype.
     */
    readonly prototype: Promise<any>;
    /**
     * Creates a new Promise.
     * @param executor A callback used to initialize the promise. This callback is passed two arguments:
     * a resolve callback used to resolve the promise with a value or the result of another promise,
     * and a reject callback used to reject the promise with a provided reason or error.
     */
   // 这里就是重点部分了
    new (executor: (resolve: (value?: T | PromiseLike) => void, reject: (reason?: any) => void) => void): Promise<T>;
  // ...
}

上面我们会看到两个其余的接口类型,分别是PromisePromiseLike,其中Promise就是实例对象的相关属性接口:

// 这时实例对象,下面只是 ES2015 的接口属性,因为后续 Promise 做过更新,后续会说明更多实例属性
interface Promise { /**
     * Attaches callbacks for the resolution and/or rejection of the Promise.
     * @param onfulfilled The callback to execute when the Promise is resolved.
     * @param onrejected The callback to execute when the Promise is rejected.
     * @returns A Promise for the completion of which ever callback is executed.
     */
    then(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): Promise<TResult1 | TResult2>;
    //...
}

PromiseLike的接口在下面:

interface PromiseLike {
    /**
     * Attaches callbacks for the resolution and/or rejection of the Promise.
     * @param onfulfilled The callback to execute when the Promise is resolved.
     * @param onrejected The callback to execute when the Promise is rejected.
     * @returns A Promise for the completion of which ever callback is executed.
     */
    then(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): PromiseLike<TResult1 | TResult2>;
}

可以看出PromiseLike接口定义的就是一个拥有then()方法的对象(官方的叫法是 thenable),只要有then()方法就会将其当做一个Promise实例看待。

// 试验一下
new Promise((resolve) => {
  resolve({
    prop: 'common property',
    // 这里我们自己构造了个 then 方法,Promise 会自动为 then 方法 reslove 和 reject 函数
    then(reslove2: any) {
      reslove2('promiselike')
    }
  })
}).then((res) => {
  // 果然,被当做成了 Promise
  console.log(res) // promiselike
})

需要注意的是,Promise 内部的回调函数的异步执行机制是使用的微任务,而我们所使用的环境中并没有为我们提供微任务的相关 api,所以代码中都是使用setTimeout进行异步模拟,将回调直接推入到事件环的最后。

如果对事件环与微任务不太了解,可以查看下这篇文章 彻底搞懂 JS 事件轮询:https://juejin.im/post/6844904198581010439。

下面是代码实现:

// 创建一枚举类型保存响应状态的变量
enum Status {
  PENDING = 'pending',
  FULFILLED = 'fulfilled',
  REJECTED = 'rejected'
}

// 将需要类型提出来
type Resolve = (value: T | PromiseLike) => voidtype Reject = (reason?: any) => voidtype Executor = (resolve: Resolve, reject: Reject) => voidtype onFulfilled =
  | ((value: T) => TResult1 | PromiseLike)
  | undefined
  | nulltype onRejected<TResult2> =
  | ((reason: any) => TResult2 | PromiseLike)
  | undefined
  | null
/* 
 将判断是否为 thenable 单独提出来,减少代码冗余,不然每次都需要使用:
 ((typeof value === 'object' && value !== null) ||typeof value === 'function') && typeof (value as PromiseLike).then === 'function'
  来进行判断,同时也有更好的 typescript 提示
*/function isPromise(value: any): value is PromiseLike<any> { return (
    ((typeof value === 'object' && value !== null) ||typeof value === 'function') &&typeof value.then === 'function')
}class MyPromise<T> {
  // 刚开始的状态status: Status = Status.PENDING
  // 保存当前 Promise 的终值,这里让它一定会有值private value!: T
  // 保存当前 Promise 的据因private reason?: anyprivate onFulfilledCallback: (() => void)[] = [] //成功的回调private onRejectedCallback: (() => void)[] = [] //失败的回调constructor(executor: Executor) { try {
      // 防止 this 丢失executor(this._resolve.bind(this), this._reject.bind(this))
    } catch (e) {
      // 出错直接 rejectthis._reject(e)
    }
  }private _resolve(value: T | PromiseLike) { try{
      // 模拟微任务异步setTimeout(() => { // 判断是否是个 thenable 对象,如果是,我们直接取 pending 结束后的值if (isPromise(value)) { // 再次将内部的 resolve 和 reject 函数传入
          value.then(this._resolve.bind(this), this._reject.bind(this))return
        }// 如果是 pending 状态就变为 fulfilledif (this.status === Status.PENDING) { this.status = Status.FULFILLED// 这里的 value 类型只会是 Tthis.value = value// resolve 后执行 .then 时传入的回调this.onFulfilledCallback.forEach((fn) => fn())
        }
      })
    }catch(err){
      // 捕获如果传入的是 Promise 时在内部抛出错误后的捕获this._reject(err)
    }
  }
  // 内部的 reject 函数,就是我们实例 Promise 传入给用户调用的 rejectprivate _reject(reason: any) {
    // 大体用法同上,这里不用进行值穿透,所以不用判断是否为 Promise 对象了setTimeout(() => { if (this.status === Status.PENDING) { this.status = Status.REJECTEDthis.reason = reasonthis.onRejectedCallback.forEach((fn) => fn())
      }
    })
  }public then<TResult1 = T, TResult2 = never>(
    onfulfilled?: onFulfilled,
    onrejected?: onRejected): MyPromise<TResult1 | TResult2> {
    //  关于 onfulfilled 与 onrejected 如果没有传我们需要进行值的透传,但是在基本功能的实现中我们先不管这个问题,默认一定会传入函数
    // 判断当前状态,如果是异步 reslove 或 reject,那么此时的 status 还是 pendingif (this.status === Status.FULFILLED) { setTimeout(() => {
        onfulfilled!(this.value)
      })
    }if (this.status === Status.REJECTED) { setTimeout(() => {
        onrejected!(this.reason)
      })
    }if (this.status === Status.PENDING) {
      // 如果为 pending,需要将 onFulfilled 和 onRejected 函数都存放起来,状态确定后再依次执行
      // 执行回调的时候有 setTimeout,这里就不加了this.onFulfilledCallback.push(() => {
        onfulfilled!(this.value)
      })this.onRejectedCallback.push(() => {
        onrejected!(this.reason)
      })
    }
    // 链式调用,这段代码现在可以
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值