前言
警告:此为小白文,大佬慎看!大佬慎看!大佬慎看!
时值中秋,被安排在公司值班,不想只是刷刷抖音来荒废时间,打算研究点什么。
在日常工作中,说起promise,大家都用的飞起,但是一看到promise面试题就懵逼,上来就是一大串then问你输出什么。每次都是看完似乎明白了,过了三天再来看又懵逼了,归根结底,还是没搞清楚底层原理。
废话不多说,来吧,就趁着中秋佳节来手写一个promise.
搭个架子
刚写完“搭个架子”四个字人就懵了,要咋写呢。
还是从用法开始一点点抠吧。
最基础的用法
const p1 = new Promise((resolve, reject) => {
console.log('我执行了!')
resolve(123)
})
console.log(p1)
输出
从上面最基础的用法可以得到以下几个信息
- 有一个new关键字
- Promise的参数是一个函数,并且执行了
这里很容易就想到了用class和构造函数,上代码
class myPromise {
constructor(fn) {
fn()
}
}
const mp1 = new myPromise((resolve, reject) => {
console.log('我执行了!')
resolve(123)
})
输出
抛开报错,我们实现了上面得到的两点信息。
接下来分析我们传入的函数
(resolve, reject) => {
...
resolve(123)
}
- 整个参数是个函数,这个上面已经说了
- 这个函数参数接收两个参数resolve和reject,并且都是函数
接着写
class myPromise {
constructor(fn) {
fn(resolve, reject)
}
}
const mp1 = new myPromise((resolve, reject) => {
console.log('我执行了!')
resolve(123)
})
输出
写到这里,其实我们很清楚的知道会报错,因为根本就没有定义resolve函数和reject函数
下一步,写resolve函数和reject函数
要写在哪里呢!! constructor构造函数里面是实例化的时候初始化执行的代码,很显然这里面不适合定义这两个函数,那写在外面试试
class myPromise {
constructor(fn) {
fn(resolve, reject)
}
resolve() {
}
reject() {
}
}
const mp1 = new myPromise((resolve, reject) => {
console.log('我执行了!')
resolve(123)
})
输出
what the fuck!!!
为啥还是提示没有定义呢。
来分析一下查找过程
在class里面定义的函数,是挂载在实例上的,比如
class A {
fn1() {
console.log('fn1')
}
}
const a = new A()
a.fn1()
到这里,好像有点眉目了,既然实例上可以访问到class定义的方法,那么我们在constructor里面也改成在实例上调用不就好了。
我们通常在constructor函数里面给实例初始化,通常写法如下:
class Dog {
constructor(name) {
this.name = name || '旺财'
}
eat() {
console.log('eat')
}
}
const dog = new Dog('大黄')
console.log(dog.name)
dog.eat()
输出
大黄
eat
这是最常见的例子,很显然,constructor里面的this就是我们创建的实例
到这里就简单了,我们只需要把resolve和reject改成从this上去不就可以了
class myPromise {
constructor(fn) {
fn(this.resolve, this.reject)
}
resolve() {
}
reject() {
}
}
const mp1 = new myPromise((resolve, reject) => {
console.log('我执行了!')
resolve(123)
})
输出
搞定,万里长征完成了第一步。喝口水休息一下。
喝完了继续。
现在我们resolve和reject函数里面还是空空如也。得填补起来。
补啥呢,又懵逼了,还是老方法,从用法开始分析吧。
我们看看我们的输出的原生的promise有啥区别。
原生promise基础使用
const p1 = new Promise((resolve, reject) => {
console.log('我执行了!')
resolve(123)
})
console.log(p1)
输出
这啥玩意...
一个一个来分析
- Prototype 从字面上看知道这是实例的原型 Promise
- PromiseState 从字面上也能看出这是指状态
- PromiseResult 从字面上看知道这是结果
好了,第一个Prototype就不细表了,相信大家也都知道,p1是Promise new 出来的实例。
第二个,Promise这个单词有承诺的意思,承诺都是许诺未来的事情,所以它必然有一个状态。在Promise中有三种状态。
- pending 等待
- fulfilled 已成功
- rejected 已失败
我们在上面的例子中已经看到了这个很熟悉的单词fulfilled,它就是表示已成功,也就是这个承诺做到了,那么自然而然它也就结束了。
接下来我们就实现这三种状态。有如下约定:
- pending 可以转化为 fulfilled,也可以转化为rejected
- 转化行为是不可逆的,一旦变更就无法修改
先来定义三种状态,这里又有一个问题,这三种状态定义在哪里呢,是在class里面还是外面。
貌似都可以,我们先放里面,实现它再说,也刚好试试class的静态属性
class myPromise {
static PENDING = 'pending'
static FULFILLED = 'fulfilled'
static REJECTED = 'rejected'
constructor(fn) {
fn(this.resolve, this.reject)
}
resolve() {
}
reject() {
}
}
const mp1 = new myPromise((resolve, reject) => {
console.log('我执行了!')
resolve(123)
})
PromiseState,这个是实例的状态,所以要定义在constructor构造函数里面。
class myPromise {
static PENDING = 'pending'
static FULFILLED = 'fulfilled'
static REJECTED = 'rejected'
constructor(fn) {
this.PromiseState = myPromise.PENDING
fn(this.resolve, this.reject)
}
resolve() {
}
reject() {
}
}
const mp1 = new myPromise((resolve, reject) => {
console.log('我执行了!')
resolve(123)
})
定义好了三种状态,继续分析。
我们知道初始状态是pending, 那它什么时候变化呢,承诺啥时候兑现呢(当然也可以拒绝)。这就刚好要用到我们前面定义的两个函数resolve()和reject()了。
- 在调用resolve()的时候,pending转化为fulfilled
- 在调用rejected()的时候,pending转化为rejected
来吧,上代码
class myPromise {
static PENDING = 'pending'
static FULFILLED = 'fulfilled'
static REJECTED = 'rejected'
constructor(fn) {
this.PromiseState = myPromise.PENDING
fn(this.resolve, this.reject)
}
resolve() {
this.PromiseState = myPromise.FULFILLED
}
reject() {
this.PromiseState = myPromise.REJECTED
}
}
const mp1 = new myPromise((resolve, reject) => {
console.log('我执行了!')
resolve(123)
})
输出
又见红了,哎。
不过好在这个报错我们经常见,小问题,无法给undefined设置属性,也就是说咱们这个resolve()里面的this是undefined。
那为啥这个this不是我们的实例mp1呢。其实也好理解,我们看resolve调用的地方,并不是在mp1上调用的,而是在实例化传入的函数里面调用。因此这个this并不是mp1。
那我们想要的就是要它指向创建的myPromise实例,才能获取实例的PromiseState。
咋办呢,只好拿出改变this指向的三板斧。call、apply、bind
这哥仨具体用法就不在这儿赘述了,有兴趣可以自行查阅其他文章。
还是简单提一下吧,他们有几个小区别。
- call、apply 会直接调用函数执行
- bind只是修改this执行,并不会立即执行函数
好了,光着一个区别我们就pass了call和apply
class myPromise {
static PENDING = 'pending'
static FULFILLED = 'fulfilled'
static REJECTED = 'rejected'
constructor(fn) {
this.PromiseState = myPromise.PENDING
fn(this.resolve.bind(this), this.reject.bind(this))
}
resolve() {
this.PromiseState = myPromise.FULFILLED
}
reject() {
this.PromiseState = myPromise.REJECTED
}
}
const mp1 = new myPromise((resolve, reject) => {
console.log('我执行了!')
resolve(123)
})
console.log(mp1)
输出
看到这里,是不是很像我们上面原生promise的输出了。
来对比看看。
先看看原生promise的表现
const p1 = new Promise((resolve, reject) => {
resolve(123)
reject(456)
})
console.log(p1)
输出
再看看我们的
class myPromise {
static PENDING = 'pending'
static FULFILLED = 'fulfilled'
static REJECTED = 'rejected'
constructor(fn) {
this.PromiseState = myPromise.PENDING
fn(this.resolve.bind(this), this.reject.bind(this))
}
resolve() {
this.PromiseState = myPromise.FULFILLED
}
reject() {
this.PromiseState = myPromise.REJECTED
}
}
const mp1 = new myPromise((resolve, reject) => {
console.log('我执行了!')
resolve(123)
reject(456)
})
console.log(mp1)
输出
是不是发现问题了,还记得前面的约定嘛。状态一经改变就不可再更改。
下面我们来解决这个问题。只在状态为pending的时候往下执行。
class myPromise {
static PENDING = 'pending'
static FULFILLED = 'fulfilled'
static REJECTED = 'rejected'
constructor(fn) {
this.PromiseState = myPromise.PENDING
fn(this.resolve.bind(this), this.reject.bind(this))
}
resolve() {
if (this.PromiseState === myPromise.PENDING) {
this.PromiseState = myPromise.FULFILLED
}
}
reject() {
if (this.PromiseState === myPromise.PENDING) {
this.PromiseState = myPromise.REJECTED
}
}
}
const mp1 = new myPromise((resolve, reject) => {
console.log('我执行了!')
resolve(123)
reject(456)
})
console.log(mp1)
输出
搞定。
等等!还记得这张图嘛
我们还有个promiseResult没实现呢。搞它
我们通过原生的应用发现,PromiseResult和PromiseState一样都是挂载在myPromise实例上。
加上去
class myPromise {
static PENDING = 'pending'
static FULFILLED = 'fulfilled'
static REJECTED = 'rejected'
constructor(fn) {
this.PromiseState = myPromise.PENDING
this.PromiseResult = null
fn(this.resolve.bind(this), this.reject.bind(this))
}
resolve() {
if (this.PromiseState === myPromise.PENDING) {
this.PromiseState = myPromise.FULFILLED
}
}
reject() {
if (this.PromiseState === myPromise.PENDING) {
this.PromiseState = myPromise.REJECTED
}
}
}
const mp1 = new myPromise((resolve, reject) => {
console.log('我执行了!')
resolve(123)
reject(456)
})
console.log(mp1)
加上去了,但这玩意儿是干嘛的呢。再回去看看我们原生的例子。
const p1 = new Promise((resolve, reject) => {
console.log('我执行了!')
resolve(123)
})
console.log(p1)
输出
好办了,这个PromiseResult只是我们resolve()或者reject()的值,我们给他们取个名字,成功的给结果(result),失败的给原因(reason)
改!
class myPromise {
static PENDING = 'pending'
static FULFILLED = 'fulfilled'
static REJECTED = 'rejected'
constructor(fn) {
this.PromiseState = myPromise.PENDING
this.PromiseResult = null
fn(this.resolve.bind(this), this.reject.bind(this))
}
resolve(result) {
if (this.PromiseState === myPromise.PENDING) {
this.PromiseState = myPromise.FULFILLED
this.PromiseResult = result
}
}
reject(reason) {
if (this.PromiseState === myPromise.PENDING) {
this.PromiseState = myPromise.REJECTED
this.PromiseResult = reason
}
}
}
const mp1 = new myPromise((resolve, reject) => {
console.log('我执行了!')
resolve(123)
})
console.log(mp1)
输出
有那味了。 我们试试失败的情况。
class myPromise {
static PENDING = 'pending'
static FULFILLED = 'fulfilled'
static REJECTED = 'rejected'
constructor(fn) {
this.PromiseState = myPromise.PENDING
this.PromiseResult = null
fn(this.resolve.bind(this), this.reject.bind(this))
}
resolve(result) {
if (this.PromiseState === myPromise.PENDING) {
this.PromiseState = myPromise.FULFILLED
this.PromiseResult = result
}
}
reject(reason) {
if (this.PromiseState === myPromise.PENDING) {
this.PromiseState = myPromise.REJECTED
this.PromiseResult = reason
}
}
}
const mp1 = new myPromise((resolve, reject) => {
resolve(123)
})
const mp2 = new myPromise((resolve, reject) => {
reject(456)
})
console.log(mp1)
console.log(mp2)
输出
大功告成,第一段落结束。
搞then
才三点,喝口水继续干。 接下来开始写then方法,还是老样子,我们从用法开始分析。 用过的朋友都知道,then方法接收两个函数参数。
const p1 = new Promise((resolve, reject) => {
resolve(123)
})
const p2 = new Promise((resolve, reject) => {
reject(456)
})
p1.then((result) => {
console.log(result)
console.log(p1)
}, (reason) => {
console.log(reason)
})
p2.then((result) => {
console.log(result)
}, (reason) => {
console.log(reason)
console.log(p2)
})
输出
看到这儿,好像发现了些什么,这个result和reason不就是咱们前面写的PromiseResult嘛。
也就是说,咱们的PromiseResult给了这个then方法第一个函数参数或者第二个函数参数的参数,好像有点拗口,能理解就行。
到这里,咱们得到几点信息:
- then方法是挂载在咱们实例上的
- then 方法接收两个函数参数
- 这两个个函数参数的参数是咱们PromiseResult的值
- 如果PromiseState的状态变为fulfilled,就执行第一个函数参数
- 如果PromiseState的状态变为rejected,就执行第二个函数参数
好吧,开搞.
我们将他们命名为onFulfilled和onRejected。
class myPromise {
static PENDING = 'pending'
static FULFILLED = 'fulfilled'
static REJECTED = 'rejected'
constructor(fn) {
this.PromiseState = myPromise.PENDING
this.PromiseResult = null
fn(this.resolve.bind(this), this.reject.bind(this))
}
resolve(result) {
if (this.PromiseState === myPromise.PENDING) {
this.PromiseState = myPromise.FULFILLED
this.PromiseResult = result
}
}
reject(reason) {
if (this.PromiseState === myPromise.PENDING) {
this.PromiseState = myPromise.REJECTED
this.PromiseResult = reason
}
}
then(onFulfilled, onRejected) {
if (this.PromiseState === myPromise.FULFILLED) {
onFulfilled(this.PromiseResult)
}
if (this.PromiseState === myPromise.REJECTED) {
onRejected(this.PromiseResult)
}
}
}
const mp1 = new myPromise((resolve, reject) => {
resolve(123)
})
const mp2 = new myPromise((resolve, reject) => {
reject(456)
})
mp1.then(result => {
console.log(result)
console.log(mp1)
}, reason => {
console.log(reason)
})
mp2.then(result => {
console.log(result)
}, reason => {
console.log(reason)
console.log(mp2)
})
输出
到这里,发现和我们原生的基本上差不多了。
下面我们来测试一下异常的情况,因为promise里面的代码不可控。
我们加一段错误的代码
class myPromise {
static PENDING = 'pending'
static FULFILLED = 'fulfilled'
static REJECTED = 'rejected'
constructor(fn) {
this.PromiseState = myPromise.PENDING
this.PromiseResult = null
fn(this.resolve.bind(this), this.reject.bind(this))
}
resolve(result) {
if (this.PromiseState === myPromise.PENDING) {
this.PromiseState = myPromise.FULFILLED
this.PromiseResult = result
}
}
reject(reason) {
if (this.PromiseState === myPromise.PENDING) {
this.PromiseState = myPromise.REJECTED
this.PromiseResult = reason
}
}
then(onFulfilled, onRejected) {
if (this.PromiseState === myPromise.FULFILLED) {
onFulfilled(this.PromiseResult)
}
if (this.PromiseState === myPromise.REJECTED) {
onRejected(this.PromiseResult)
}
}
}
const mp1 = new myPromise((resolve, reject) => {
console.log(abc)
resolve(123)
})
const mp2 = new myPromise((resolve, reject) => {
reject(456)
})
mp1.then(result => {
console.log('fulfilled:' + result)
console.log(mp1)
}, reason => {
console.log('rejected:' + reason)
})
mp2.then(result => {
console.log('fulfilled:' + result)
}, reason => {
console.log('rejected:' + reason)
console.log(mp2)
})
输出
啊哦,报错了,对比一下原生promise,看会输出什么
const p1 = new Promise((resolve, reject) => {
console.log(abc)
resolve(123)
})
const p2 = new Promise((resolve, reject) => {
reject(456)
})
p1.then((result) => {
console.log('fulfilled:' + result)
console.log(p1)
}, (reason) => {
console.log('rejected:' + reason)
})
p2.then((result) => {
console.log('fulfilled:' + result)
}, (reason) => {
console.log('rejected:' + reason)
console.log(p2)
})
输出
可以看到原生的promise处理,虽然有报错,依然往后执行了reject。
我们可以做如下处理:用try catch来捕获可能出现的错误,然后手动执行reject
class myPromise {
static PENDING = 'pending'
static FULFILLED = 'fulfilled'
static REJECTED = 'rejected'
constructor(fn) {
this.PromiseState = myPromise.PENDING
this.PromiseResult = null
try {
fn(this.resolve.bind(this), this.reject.bind(this))
} catch (error) {
this.reject(error)
}
}
resolve(result) {
if (this.PromiseState === myPromise.PENDING) {
this.PromiseState = myPromise.FULFILLED
this.PromiseResult = result
}
}
reject(reason) {
if (this.PromiseState === myPromise.PENDING) {
this.PromiseState = myPromise.REJECTED
this.PromiseResult = reason
}
}
then(onFulfilled, onRejected) {
if (this.PromiseState === myPromise.FULFILLED) {
onFulfilled(this.PromiseResult)
}
if (this.PromiseState === myPromise.REJECTED) {
onRejected(this.PromiseResult)
}
}
}
const mp1 = new myPromise((resolve, reject) => {
console.log(abc)
resolve(123)
})
const mp2 = new myPromise((resolve, reject) => {
reject(456)
})
mp1.then(result => {
console.log('fulfilled:' + result)
console.log(mp1)
}, reason => {
console.log('rejected:' + reason)
})
mp2.then(result => {
console.log('fulfilled:' + result)
}, reason => {
console.log('rejected:' + reason)
console.log(mp2)
})
输出
得到了我们想要的结果,和原生promise表现一致了。
但是仔细一想,好像又有点不对劲。
我们平时的习惯,好像很少写then的第二个参数,而是在then后面写catch函数。
这又是啥玩意呢。上代码,看看原生的
const p1 = new Promise((resolve, reject) => {
reject(456)
})
p1.then((result) => {
console.log('fulfilled:' + result)
console.log(p1)
}, (reason) => {
console.log('rejected:' + reason)
})
p1.catch(error => {
console.log('error:' + error)
})
输出
看到这里,神奇的发现,then方法的第二个函数参数和catch方法的函数参数都执行了!!!
是不是这两个是同一个东西呢。
我们再来试验一下另一种情况。
const p1 = new Promise((resolve, reject) => {
console.log(abc)
reject(456)
})
p1.then((result) => {
console.log('fulfilled:' + result)
console.log(p1)
}, (reason) => {
console.log('rejected:' + reason)
})
p1.catch(error => {
console.log('error:' + error)
})
输出
到这里可以发现,catch的行为和then的第二个函数参数是一致的。那我们可以先简单认为catch是then第二个函数参数的一个语法糖。
来手写一个试试
class myPromise {
static PENDING = 'pending'
static FULFILLED = 'fulfilled'
static REJECTED = 'rejected'
constructor(fn) {
this.PromiseState = myPromise.PENDING
this.PromiseResult = null
try {
fn(this.resolve.bind(this), this.reject.bind(this))
} catch (error) {
this.reject(error)
}
}
resolve(result) {
if (this.PromiseState === myPromise.PENDING) {
this.PromiseState = myPromise.FULFILLED
this.PromiseResult = result
}
}
reject(reason) {
if (this.PromiseState === myPromise.PENDING) {
this.PromiseState = myPromise.REJECTED
this.PromiseResult = reason
}
}
then(onFulfilled, onRejected) {
if (this.PromiseState === myPromise.FULFILLED) {
onFulfilled(this.PromiseResult)
}
if (this.PromiseState === myPromise.REJECTED) {
onRejected(this.PromiseResult)
}
}
catch(onError) {
this.then(null, onError)
}
}
const mp1 = new myPromise((resolve, reject) => {
console.log(abc)
resolve(123)
})
mp1.then(result => {
console.log('fulfilled:' + result)
console.log(mp1)
}, reason => {
console.log('rejected:' + reason)
})
mp1.catch(error => {
console.log('error:' + error)
})
输出
是不是到这里就完美了呢,再测试一下不走reject的情况
class myPromise {
static PENDING = 'pending'
static FULFILLED = 'fulfilled'
static REJECTED = 'rejected'
constructor(fn) {
this.PromiseState = myPromise.PENDING
this.PromiseResult = null
try {
fn(this.resolve.bind(this), this.reject.bind(this))
} catch (error) {
this.reject(error)
}
}
resolve(result) {
if (this.PromiseState === myPromise.PENDING) {
this.PromiseState = myPromise.FULFILLED
this.PromiseResult = result
}
}
reject(reason) {
if (this.PromiseState === myPromise.PENDING) {
this.PromiseState = myPromise.REJECTED
this.PromiseResult = reason
}
}
then(onFulfilled, onRejected) {
if (this.PromiseState === myPromise.FULFILLED) {
onFulfilled(this.PromiseResult)
}
if (this.PromiseState === myPromise.REJECTED) {
onRejected(this.PromiseResult)
}
}
catch(onError) {
this.then(null, onError)
}
}
const mp1 = new myPromise((resolve, reject) => {
resolve(123)
})
const mp2 = new myPromise((resolve, reject) => {
reject(456)
})
mp1.then(result => {
console.log('fulfilled:' + result)
console.log(mp1)
}, reason => {
console.log('rejected:' + reason)
})
mp1.catch(error => {
console.log('error:' + error)
})
输出
到这儿,出现报错了,这个问题也很容易理解,我们在调用catch的时候,相当于调用了then,然后第一个参数传入了null,在执行onFulfilled(this.PromiseResult)的时候出现了问题。
像这类问题,很容易想到加上类型判断。
要处理传入的不为function的情况。我们做以下处理:
- 如果传入的是function,就原封不动的返回这个function
- 如果传入的不是function
- 把第一个参数onFulfilled包装成函数,返回这个值
- 把第二个参数onRejected同样包装成函数,在函数体中抛出这个错误
class myPromise {
static PENDING = 'pending'
static FULFILLED = 'fulfilled'
static REJECTED = 'rejected'
constructor(fn) {
this.PromiseState = myPromise.PENDING
this.PromiseResult = null
try {
fn(this.resolve.bind(this), this.reject.bind(this))
} catch (error) {
this.reject(error)
}
}
resolve(result) {
if (this.PromiseState === myPromise.PENDING) {
this.PromiseState = myPromise.FULFILLED
this.PromiseResult = result
}
}
reject(reason) {
if (this.PromiseState === myPromise.PENDING) {
this.PromiseState = myPromise.REJECTED
this.PromiseResult = reason
}
}
then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
onRejected = typeof onRejected === 'function' ? onRejected : (reason) => {
throw reason
}
if (this.PromiseState === myPromise.FULFILLED) {
onFulfilled(this.PromiseResult)
}
if (this.PromiseState === myPromise.REJECTED) {
onRejected(this.PromiseResult)
}
}
catch(onError) {
this.then(null, onError)
}
}
const mp1 = new myPromise((resolve, reject) => {
resolve(123)
})
mp1.then(result => {
console.log('fulfilled:' + result)
console.log(mp1)
}, reason => {
console.log('rejected:' + reason)
})
mp1.catch(error => {
console.log('error:' + error)
})
输出
到这里,我们的实现算是告一段落了。
等等,总感觉哪里不对劲,咱们promise的核心不是用来解决异步问题的嘛,怎么从头到尾都是同步代码。
也就是我们写了那么多,还没有触及核心问题。
莫慌,我们上面写的东西已经把promise的基础框架搭建好了,接下来就是啃异步这个硬骨头了。
还是老样子,从原生promise的用法开始看。
console.log(1)
const p = new Promise((resolve, reject) => {
console.log(2)
resolve('成功了!')
})
p.then((result) => {
console.log(result)
})
console.log(3)
输出
1
2
3
成功了
再来看看我们的
class myPromise {
static PENDING = 'pending'
static FULFILLED = 'fulfilled'
static REJECTED = 'rejected'
constructor(fn) {
this.PromiseState = myPromise.PENDING
this.PromiseResult = null
try {
fn(this.resolve.bind(this), this.reject.bind(this))
} catch (error) {
this.reject(error)
}
}
resolve(result) {
if (this.PromiseState === myPromise.PENDING) {
this.PromiseState = myPromise.FULFILLED
this.PromiseResult = result
}
}
reject(reason) {
if (this.PromiseState === myPromise.PENDING) {
this.PromiseState = myPromise.REJECTED
this.PromiseResult = reason
}
}
then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
onRejected = typeof onRejected === 'function' ? onRejected : (reason) => {
throw reason
}
if (this.PromiseState === myPromise.FULFILLED) {
onFulfilled(this.PromiseResult)
}
if (this.PromiseState === myPromise.REJECTED) {
onRejected(this.PromiseResult)
}
}
catch(onError) {
this.then(null, onError)
}
}
console.log(1)
const mp1 = new myPromise((resolve, reject) => {
console.log(2)
resolve('成功了')
})
mp1.then((result) => {
console.log(result)
})
console.log(3)
输出
1
2
成功了
3
通过我们自己的promise和原生promise对比,可以看到执行结果的区别在于resolve的值。
我们自己的很好理解,就是按照顺序同步执行下来。
接下来对我们自己的promise做改造,让它能符合原生promise的执行顺序。
resolve的结果需要在当前执行栈所有同步代码执行完毕后执行。这里我们第一个想到的是setTimeout,
既然我们像要resolve()结果的输出在当前栈同步代码之后,先给resolve函数里面的代码用setTimeout包裹一下。
class myPromise {
static PENDING = 'pending'
static FULFILLED = 'fulfilled'
static REJECTED = 'rejected'
constructor(fn) {
this.PromiseState = myPromise.PENDING
this.PromiseResult = null
try {
fn(this.resolve.bind(this), this.reject.bind(this))
} catch (error) {
this.reject(error)
}
}
resolve(result) {
if (this.PromiseState === myPromise.PENDING) {
setTimeout(() => {
this.PromiseState = myPromise.FULFILLED
this.PromiseResult = result
})
}
}
reject(reason) {
if (this.PromiseState === myPromise.PENDING) {
setTimeout(() => {
this.PromiseState = myPromise.REJECTED
this.PromiseResult = reason
})
}
}
then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
onRejected = typeof onRejected === 'function' ? onRejected : (reason) => {
throw reason
}
if (this.PromiseState === myPromise.FULFILLED) {
onFulfilled(this.PromiseResult)
}
if (this.PromiseState === myPromise.REJECTED) {
onRejected(this.PromiseResult)
}
}
catch(onError) {
this.then(null, onError)
}
}
console.log(1)
const mp1 = new myPromise((resolve, reject) => {
console.log(2)
resolve('成功了')
})
mp1.then((result) => {
console.log(result)
})
console.log(3)
输出
1
2
3
诶,直接给resolve的结果干没了。分析一下执行顺序:
- 从上往下执行,先输出1
- 执行myPromise传入的函数,输出2
- 执行resolve('成功了'),进入setTimeout(() => {...}),先执行当前栈的同步代码
- 执行mp1.then(...)
- 进入then函数
- 由于修改PromiseState的代码被包裹在setTimeout中了,此时PromiseState并未改变,仍然是pending
- 此时then后面的if条件都不满足了
- 往后执行,输出3
- 最后执行setTimeout包裹的代码,修改PromiseState值为fulfilled
- 结束
通过以上分析,发现问题的关键在于mp1.then的执行在修改PromiseState状态之前。
因此,我们改造的思路要放在mp1.then的执行上。
开始改,resolve的结果的值赋给了PromiseResult,输出是在then方法的函数参数中。
因此我们可以想到用setTimeout来包裹then方法中的代码,让它们在同步代码执行完后再执行。
试试吧。
class myPromise {
static PENDING = 'pending'
static FULFILLED = 'fulfilled'
static REJECTED = 'rejected'
constructor(fn) {
this.PromiseState = myPromise.PENDING
this.PromiseResult = null
try {
fn(this.resolve.bind(this), this.reject.bind(this))
} catch (error) {
this.reject(error)
}
}
resolve(result) {
if (this.PromiseState === myPromise.PENDING) {
this.PromiseState = myPromise.FULFILLED
this.PromiseResult = result
}
}
reject(reason) {
if (this.PromiseState === myPromise.PENDING) {
this.PromiseState = myPromise.REJECTED
this.PromiseResult = reason
}
}
then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
onRejected = typeof onRejected === 'function' ? onRejected : (reason) => {
throw reason
}
if (this.PromiseState === myPromise.FULFILLED) {
setTimeout(() => {
onFulfilled(this.PromiseResult)
})
}
if (this.PromiseState === myPromise.REJECTED) {
setTimeout(() => {
onRejected(this.PromiseResult)
})
}
}
catch(onError) {
this.then(null, onError)
}
}
console.log(1)
const mp1 = new myPromise((resolve, reject) => {
console.log(2)
resolve('成功了')
})
mp1.then((result) => {
console.log(result)
})
console.log(3)
输出
1
2
3
成功了
正如结果输出的那样,成功了。
回想我们应用Promise的场景,大多数情况下,是在做异步请求的情况下
也就是resolve()是被包裹在一段异步代码中。
看看原生的例子:
console.log(1)
const p = new Promise((resolve, reject) => {
console.log(2)
setTimeout(() => {
resolve('成功了!')
console.log(4)
})
})
p.then((result) => {
console.log(result)
})
console.log(3)
输出
1
2
3
4
成功了
再来看看我们自己的Promise:
class myPromise {
static PENDING = 'pending'
static FULFILLED = 'fulfilled'
static REJECTED = 'rejected'
constructor(fn) {
this.PromiseState = myPromise.PENDING
this.PromiseResult = null
try {
fn(this.resolve.bind(this), this.reject.bind(this))
} catch (error) {
this.reject(error)
}
}
resolve(result) {
if (this.PromiseState === myPromise.PENDING) {
this.PromiseState = myPromise.FULFILLED
this.PromiseResult = result
}
}
reject(reason) {
if (this.PromiseState === myPromise.PENDING) {
this.PromiseState = myPromise.REJECTED
this.PromiseResult = reason
}
}
then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
onRejected = typeof onRejected === 'function' ? onRejected : (reason) => {
throw reason
}
if (this.PromiseState === myPromise.FULFILLED) {
setTimeout(() => {
onFulfilled(this.PromiseResult)
})
}
if (this.PromiseState === myPromise.REJECTED) {
setTimeout(() => {
onRejected(this.PromiseResult)
})
}
}
catch(onError) {
this.then(null, onError)
}
}
console.log(1)
const mp1 = new myPromise((resolve, reject) => {
console.log(2)
setTimeout(() => {
resolve('成功了')
console.log(4)
})
})
mp1.then((result) => {
console.log(result)
})
console.log(3)
输出
1
2
3
4
出现了和我们上面同样的问题,简单分析一下:
- 由于resolve('成功了')被包裹在setTimeout中NA
- 在执行mp1.then(...)的时候,PromiseState的状态仍然是pending
- then方法中的if都不满足条件,因此不会调用onFulfilled方法
- 最后执行resolve('成功了')的时候,修改了PromiseState的状态,但是此时不会再去调用onFulfilled方法了
这里碰到一个比较棘手的问题:如果resolve()被包裹在异步代码中,在执行then方法的时候,PromiseState的状态始终是pending, 那么onFulfilled()和onRejected()都不会执行。
因此我们需要在then()中处理PromiseState为pending状态的情况。
class myPromise {
static PENDING = 'pending'
static FULFILLED = 'fulfilled'
static REJECTED = 'rejected'
constructor(fn) {
this.PromiseState = myPromise.PENDING
this.PromiseResult = null
try {
fn(this.resolve.bind(this), this.reject.bind(this))
} catch (error) {
this.reject(error)
}
}
resolve(result) {
if (this.PromiseState === myPromise.PENDING) {
this.PromiseState = myPromise.FULFILLED
this.PromiseResult = result
}
}
reject(reason) {
if (this.PromiseState === myPromise.PENDING) {
this.PromiseState = myPromise.REJECTED
this.PromiseResult = reason
}
}
then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
onRejected = typeof onRejected === 'function' ? onRejected : (reason) => {
throw reason
}
if (this.PromiseState === myPromise.PENDING) {
......
}
if (this.PromiseState === myPromise.FULFILLED) {
setTimeout(() => {
onFulfilled(this.PromiseResult)
})
}
if (this.PromiseState === myPromise.REJECTED) {
setTimeout(() => {
onRejected(this.PromiseResult)
})
}
}
catch(onError) {
this.then(null, onError)
}
}
继续分析,我们想让onFulfilled或onRejected在PromiseState状态改变之后执行,而PromiseState是在resolve()中修改的,resolve又被包裹在一段异步代码中。
所以,在then方法中,onFulfilled()或onRejected()会在未来某个时刻执行。
在执行到then()的时候,如果PromiseState是pending状态,我们需要将onFulfilled()和onRejected()先保存起来。
保存数据通常想到的是数组,这里我们定义两个数组onFulfilledCbs和onRejectedCbs来分别存储onFulfilled()和onRejected().
在then方法中,判断如果PromiseState为pending状态,就先存储起来。
然后在resolve()和reject()中,当PromiseState为结束状态的时候,去除数组中的函数并执行。
class myPromise {
static PENDING = 'pending'
static FULFILLED = 'fulfilled'
static REJECTED = 'rejected'
constructor(fn) {
this.PromiseState = myPromise.PENDING
this.PromiseResult = null
this.onFulfilledCbs = []
this.onRejectedCbs = []
try {
fn(this.resolve.bind(this), this.reject.bind(this))
} catch (error) {
this.reject(error)
}
}
resolve(result) {
if (this.PromiseState === myPromise.PENDING) {
this.PromiseState = myPromise.FULFILLED
this.PromiseResult = result
this.onFulfilledCbs.forEach(cb => {
cb(result)
})
}
}
reject(reason) {
if (this.PromiseState === myPromise.PENDING) {
this.PromiseState = myPromise.REJECTED
this.PromiseResult = reason
this.onRejectedCbs.forEach(cb => {
cb(reason)
})
}
}
then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
onRejected = typeof onRejected === 'function' ? onRejected : (reason) => {
throw reason
}
if (this.PromiseState === myPromise.PENDING) {
this.onFulfilledCbs.push(onFulfilled)
this.onRejectedCbs.push(onRejected)
}
if (this.PromiseState === myPromise.FULFILLED) {
setTimeout(() => {
onFulfilled(this.PromiseResult)
})
}
if (this.PromiseState === myPromise.REJECTED) {
setTimeout(() => {
onRejected(this.PromiseResult)
})
}
}
catch(onError) {
this.then(null, onError)
}
}
console.log(1)
const mp1 = new myPromise((resolve, reject) => {
console.log(2)
setTimeout(() => {
resolve('成功了')
console.log(4)
})
})
mp1.then((result) => {
console.log(result)
})
console.log(3)
输出
1
2
3
成功了
4
输出和我们想要的结果一致,到这里myPromise的实现算是真正告一段落了。