promise源码详解
promise对象是ES6提出来的,用来解决回调地狱问题的。今天我就来给大家讲解一下如何手写核心的promise代码。
回调地狱
首先我们来了解一下什么是回调地狱,回调地狱指的是,多级异步嵌套调用问题。举个例子:
上述图片中,第二次queryData调用会依赖第一次的结果,所以必须在第一次异步回调里面嵌套调用第二次异步方法;以此类推,多层级异步调用嵌套,就会带来回调地狱问题,会造成代码可阅读性很差,后期不好维护等问题。
promise介绍
promise对象就是用来解决回调地狱问题的,可以将异步操作以同步流程的方式表达出来,避免了层层嵌套回调函数的方式。
promise的特点
promise的状态
promise有三个状态,分别是:
- pending: 初始状态,不是成功或失败状态。
- fulfilled: 意味着操作成功完成。
- rejected: 意味着操作失败。
只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。
一旦状态改变,就不会再变,状态不可逆,任何时候都可以得到这个结果。Promise 对象的状态改变,只有两种可能:从 Pending 变为 Resolved 和从 Pending 变为 Rejected。
then链式调用
promise.then().then()可以链式调用,因为promise.then()方法会返回一个新的promise,新的promise可以继续执行then方法。
promise的优缺点
promise的优点
promise可以将异步操作以同步的方式表达出来,避免了层层嵌套回调函数的方式。
Promise缺点
首先,无法取消 Promise,一旦新建它就会立即执行,无法中途取消。其次,如果不设置回调函数,Promise 内部抛出的错误,不会反应到外部。第三,当处于 Pending 状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。
class实现手写promise
首先,我们来回顾一下原生的promise是怎么使用的?
能看出来promise是通过构造函数实例化一个对象的,构造函数里面传入一个fn函数,fn立即执行,初始化状态为pending,fn函数有resolve和reject两个参数,这两个参数也是函数,resolve调用之后pending改变为fulfilled状态,reject调用后pending改为rejected状态。
所以我就用class来实现手写的promise,用静态属性来定义状态,在回调函数resolve和reject中判断,pending状态的时候才可以更改状态,更改状态的同时,将回调函数中成功的结果或者失败的原因都保存在对应的属性result中,方便以后来获取。代码如下:
为什么会报错呢?因为this指向问题,new一个实例,this指向实例,而实例p上面没有state属性,所以提示undefined。
所以就用bind来改变一下this的指向问题。
异步
测试一下异步操作,发现,输出结果错误,没有异步,还是同步。
resolve,reject也是放在事件循环的最后执行,所以将then,resove,reject都加上setTimeout方法。代码如下:
then实现
当Promise的状态改变之后,不管成功还是失败,都会触发then回调函数。因此,then有两个参数,就是根据状态的不同,来调用不同处理终值的函数。
当then里面函数运行时,resolve由于是异步执行的,还没有来得及修改state,此时还是PENDING状态;因此我们需要对异步的情况做一下处理。参考发布订阅模式,在执行then方法的时候,如果还是PENDING状态,就把回调函数存到一个数组中,当状态发生改变时,去数组中取出回调函数依次执行。
注意一下,因为then的两个参数不一定是函数,所以要做一下判断,不是函数的话,赋值一个空函数。
另外,因为then支持链式调用,就在return 一个手写的promise就行,就能保证每次返回一个全新的promise,从而实现链式调用。
小细节
直接抛出Error的处理
new promise 传入的立即执行的函数,也会可能存在异常,因此需要通过try/catch来捕获一下异常情况,否则会直接抛出异常。
整体核心代码
到这里,手写promise的核心代码大体上完成了。当然实际上原生promise的代码比这个要复杂多了,它还涉及到Promise.all,Promise.race,Promise.prototype.catch等方法。代码如下:
class MyPromise {
static Pending = "待定";
static FulFilled = "成功";
static Rejected = "失败";
constructor(func) {
this.state = MyPromise.Pending;
this.result = null;
this.fulfilledCallbacks = [];
this.rejectedCallbacks = [];
try {
func(this.resolve.bind(this), this.reject.bind(this))
} catch (e) {
this.reject(e)
}
}
resolve (res) {
setTimeout(() => {
if (this.state == MyPromise.Pending) {
this.state = MyPromise.FulFilled;
this.result = res;
this.fulfilledCallbacks.forEach(callback => {
callback(res)
})
}
})
}
reject (reason) {
setTimeout(() => {
if (this.state == MyPromise.Pending) {
this.state = MyPromise.Rejected;
this.result = reason;
this.rejectedCallbacks.forEach(callback => {
callback(reason)
})
}
})
}
then (onFullFilled, onRejected) {
return new MyPromise((resolve, reject) => {
onFullFilled = typeof (onFullFilled) == 'function' ? onFullFilled : () => { };
onRejected = typeof (onRejected) == 'function' ? onRejected : () => { };
if (this.state == MyPromise.Pending) {
this.fulfilledCallbacks.push(onFullFilled)
this.rejectedCallbacks.push(onRejected)
}
if (this.state == MyPromise.FulFilled) {
setTimeout(() => {
onFullFilled(this.result)
})
}
if (this.state == MyPromise.Rejected) {
setTimeout(() => {
onRejected(this.result)
})
}
})
}
}
如果写的有问题的,欢迎大家指正,一起讨论。