模拟实现 Promise(小白版) (2)

注意:骨架这里的代码,我用了 TypeScript,这是一种强类型语言,可以标明各个变量、参数类型,便于讲述和理解,看不懂没关系,下面有编译成 js 版的

所以,我们要补充完成的其实就是三部分:Promise 构造函数都做了哪些事、状态变更需要做什么处理、then 注册回调函数时需要做的处理

第二步:构造函数

Promise 的构造函数做的事,其实很简单,就是马上执行传入的 task 处理函数,并将自己内部提供的两个状态变更处理的函数传递给 task,同时将当前 promise 状态置为 PENDING(执行中)

constructor(task) { // 1. 将当前状态置为 PENDING this._status = this.PENDING; // 参数类型校验 if (!(task instanceof Function)) { throw new TypeError(`${task} is not a function`); } try { // 2. 调用 task 处理函数,并将状态变更通知的函数传递过去,需要注意 this 的处理 task(this._handleResolve.bind(this), this._handleReject.bind(this)); } catch (e) { // 3. 如果 task 处理函数发生异常,当做失败来处理 this._handleReject(e); } } 第三步:状态变更

Promise 状态变更的相关处理是我觉得实现 Promise 最难的一部分,这里说的难并不是说代码有多复杂,而是说这块需要理解透,或者看懂规范并不大容易,因为需要考虑一些处理,网上看了些 Promise 实现的文章,这部分都存在问题

状态变更的工作,是由传给 task 处理函数的两个函数参数被调用时触发进行,如:

new Promise((resolve, reject) => { resolve(1); });

resolve 或 reject 的调用,就会触发 Promise 内部去处理状态变更的相关工作,还记得构造函数做的事吧,这里的 resolve 或 reject 其实就是对应着内部的 _handleResolve 和 _handleReject 这两个处理状态变更工作的函数

但这里有一点需要注意,是不是 resolve 一调用,Promise 的状态就一定发生变化了呢?

答案不是的,网上看了些这类文章,他们的处理是 resolve 调用,状态就变化,就去处理回调队列了

但实际上,这样是错的

状态的变更,其实依赖于 resolve 调用时,传递过去的参数的类型,因为这里可以传递任意类型的值,可以是基本类型,也可以是 Promise

当类型不一样时,对于状态的变更处理是不一样的,开头那篇规范里面有详细的说明,但要看懂并不大容易,我这里就简单用我的理解来讲讲:

resolve(x) 触发的 pending => resolved 的处理:

当 x 类型是 Promise 对象时:

当 x 这个 Promise 的状态变化结束时,再以 x 这个 Promise 内部状态和结果(_status 和 _value)作为当前 Promise 的状态和结果进行状态变更处理

可以简单理解成当前的 Promise 是依赖于 x 这个 Promise 的,即 x.then(this._handleResolve, this._handleReject)

当 x 类型是 thenable 对象(具有 then 方法的对象)时:

把这个 then 方法作为 task 处理函数来处理,这样就又回到第一步即等待状态变更的触发

可以简单理解成 x.then(this._handleResolve, this._handleReject)

这里的 x.then 并不是 Promise 的 then 处理,只是简单的一个函数调用,只是刚好函数名叫做 then

其余类型时:

内部状态(_status)置为 RESOLVE

内部结果(_value)置为 x

模拟创建微任务(setTimeout)处理回调函数队列

reject(x) 触发的 pending => rejected 的处理:

不区分 x 类型,直接走 rejected 的处理

内部状态(_status)置为 REJECTED

内部结构(_value)置为 x

模拟创建微任务(setTimeout)处理回调函数队列

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zyzzgw.html