java promise实现,JS Promise的用法, 以及自己模拟一个Promise

注: 本文中写的类只是为了了解Promise类的内部原理而模拟出来一个, 并不一定符合类似的规范或者效率多么高, 但是基本的功能还是实现了的.

注: 本文代码运行环境: NodeJS v14.9.0

用法

如下, 这是一个传统的使用回调函数的异步代码

function getAnInt(callback) { setTimeout(() => { callback(81) }, 500)}function sqrt(n, resolve, reject) { setTimeout(() => { let res = Math.sqrt(n) if (parseInt(res) === res) { resolve(res) } else { reject("cannot get an int") } }, 500)}let errHandler = err => console.log("Error " + err)getAnInt(v1 => { console.log(v1) sqrt(v1, v2 => { console.log(v2) sqrt(v2, v3 => { console.log(v3) sqrt(v3, v4 => { console.log(v4) }, errHandler) }, errHandler) }, errHandler)})

执行结果:

8193Error cannot get an int

有没有感觉眼花缭乱? 这金字塔状的代码被亲切地称为回调地狱, 下面就是我们的主角Promise上场的时候了, 酱酱酱酱

function getAnInt() { return new Promise((resolve, reject) => { setTimeout(() => { resolve(81) }, 500) })}function sqrt(n) { return new Promise((resolve, reject) => { setTimeout(() => { let res = Math.sqrt(n) if (parseInt(res) === res) { resolve(res) } else { reject("cannot get an int") } }, 500) })}getAnInt().then(v1 => { console.log(v1) return sqrt(v1)}).then(v2 => { console.log(v2) return sqrt(v2)}).then(v3 => { console.log(v3) return sqrt(v3)}).then(v4 => { console.log(v4)}).catch(err => { console.log("Error " + err)})

执行结果:

8193Error cannot get an int

结果一模一样, 但是这个代码写出来的感觉, 就是要清晰了好多好多好多好多好多好多好多好多好多好多好多好多

介绍

在Promise/A+标准中定义了Promise到底是个什么东西, 这里挑出重点部分, 其余的规范如果想看的话点这里去官网

promise 含有then方法, 没有规定其它的方法.

then方法会返回一个新的promise

then方法的参数是onFulfilled, onRejected, 它们都是可选的(当然都是函数类型)

promise有三个状态, pending(代办), fulfilled(完成) 和 rejected(被拒绝), 状态只能从pending转成另外两个, 然后就不能再转了.

如果onRejected或者onFulfilled返回了一个Promise对象, 需要得出它的结果再传给下一个then方法里对应的地方

因为本文代码中有很多的 resolve, 所以这里的代码使用resolved(被解决)代替fulfilled

为什么没有列出来更多的内容呢, 因为其它的内容大多和兼容性有关, 与这个实现原理关系不是太大, 还有的是到具体实现函数的时候才会用到的规范, 所以我没有列出来

注: catch方法是ES6标准里的, 它的原理是then(null, onRejected)

实现

注: 本文代码不考虑throw, 为了只体现原理, 让代码尽可能更简单.

构造函数

Promise的构造函数通常传入一个执行者函数, 这个函数里面可能是异步逻辑(这么说的意思就是也可能不是), 接受两个参数: resolve和reject.

调用resolve(value)就代表方法成功执行了, Promise会把resolve中传入的value传给then方法里的参数

调用reject(reason)就是执行出错了, Promise会把reject中传入的reason传给then方法里的参数

好, 下面开始做点准备工作

const Pending = 'pending'const Resolved = 'resolved'const Rejected = 'rejected'class MyPromise {}

诶, 这段代码我感觉不用解释了吧? 下面的我会在注释或者是代码块下方说明

class MyPromise { constructor(executor) { // 状态 this.status = Pending // 正常运行返回的结果 this.value = null // 发生错误的原因 this.reason = null // 详见这段代码块下面写的 注1 this.onRejected = () => {} this.onResolved = () => {} let resolve = value => { // 如果不是Pending就忽略 if (this.status !== Pending) { return } this.status = Resolved this.value = value this.onResolved(value) } let reject = reason => { // 如果不是Pending就忽略 if (this.status !== Pending) { return } this.status = Rejected this.reason = reason this.onRejected(reason) } // 见 注2 executor(resolve, reject) }}

注1: 这是两个被reject或者resolve后调用的回调函数, 我看的别人实现的版本大多是一个数组, 然后调用的时候一个接一个调用里面的函数.

我认为对同一个promise调用多次then方法的时候很少, 而且本文只是一个思路展示, 并不严格遵守A+规范, 所以这里就直接写了个什么也没干的函数

在这里也分析一下, 在then方法调用的时候, 如果调用then时的状态是Pending, 那么就设置一下当前对象里的onRejected和onResolved, 具体设置什么在后面的代码里会提到; 如果状态不是Pending, 就代表这两个函数早就执行完了, 就需要根据this.value和this.reason具体的调用then函数中传进来的onRejected和onResolved.

注2: 这里直接同步调用了, 没有异步调用. 因为如果这个操作真的需要异步的话, 在executor函数里面就会有异步方法了(如setTimeout), 不需要Promise类给它办.

Then方法

然后就是then方法啦~

注意: then方法要求每次返回新的Promise对象.

先写个框架

then(onResolved, onRejected) { let funcOrNull = f => typeof f === "function" ? f : null onResolved = funcOrNull(onResolved) onRejected = funcOrNull(onRejected) if (this.status === Rejected) { return new MyPromise((resolve, reject) => { }) } else if (this.status === Resolved) { return new MyPromise((resolve, reject) => { }) } else { return new MyPromise((resolve, reject) => { }) }}

Rejected

如果是状态是rejected, 那么

if (this.status === Rejected) { return new MyPromise((resolve, reject) => { let value = (onRejected === null ? reject : onRejected)(this.reason) if (value instanceof MyPromise) { value.then(resolve, reject) } else { resolve(value) } })}

这些实现的代码包括下面的elseif和else块就是最难理解的了, 我当时是好久好久也没有理解, 接下来我会就像数学里面一样分类讨论:

关于Rejected块的详细说明(尽管也就10行)

理解了Rejected块, 那么Resolved块和他几乎一模一样, 只是函数名字不一样而已, 所以我这里会分析的尽可能详细

如果调用的时候是这样的:new MyPromise((resolve, reject) => { reject("I rejected the promise")}).then(null, console.log)

先分析构造方法, 创建Promise对象的时候, 这里它的状态就变成Rejected, 但是其他的什么事都没干, 让我们来看前面的代码this.onRejected = () => {}this.onResolved = () => {}let reject = reason => { if (this.status !== Pending) { return } this.status = Rejected this.reason = reason this.onRejected(reason)}executor(resolve, reject)

这个时候this.onRejected还是个空函数, 所以调用它也没什么用

接下来到then方法了, 让我们来看上面if块里的代码return new MyPromise((resolve, reject) => { let value = (onRejected === null ? reject : onRejected)(this.reason) if (value instanceof MyPromise) { value.then(resolve, reject) } else { resolve(value) }})

可以看出它执行了let value = onRejected(reason), 然后调用resolve(value), 之后这个新的Promise状态就是Resolved了.

至于为什么这里要用resolve, 我是通过NodeJS做了个实验看看NodeJS对这件事是怎么干的, 代码如下let p1 = new Promise((resolve, reject) => { reject("I rejected the promise")})let p2 = p1.then(null, reason => { return 'I am from onRejected function'})// 这里是为了不管到底是什么状态都能把p1和p2输出出来p2.then(() => console.log(p1, p2), () => console.log(p1, p2))

输出(原本的执行结果没有换行, 我为了方便看自己加上的)Promise { 'I rejected the promise' }Promise { 'I am from onRejected function' }

这.........

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值