写在前面
本篇文章将会带大家从分解promise入手,一步步实现一个promise。但阅读之前需要比较熟练地了解了解用法,结合用法看文章可能更容易理解。
结构
先看一下简单的用法。
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('success')
})
})
.then(value => { ... }, reason => { ... })
.catch(error => { ... })
复制代码
Promise的构造函数接收了一个回调,这个回调就是下面要讲到的执行器,执行器的参数resolve, reject也是两个函数, 负责改变promise实例的状态和它的值,then函数中的回调在状态改变后执行。
注意:不是then函数在状态改变后执行,而是then中的回调函数在状态改变后执行。then方法会将其中的回调放入执行队列,promise的状态改变后再将队列中的函数一一执行。
如果要实现一个最简单的promise类,内部结构都要包含什么呢?
状态:fulfiled、rejected、pending。
值:promise的值。
执行器:提供改变promise状态的入口。
resolve和reject方法:前者将promise改变为fulfiled,后者将其改变为rejected。可以在执行器内根据实际业务来控制是resolve或reject。
then方法:接收两个回调,onFulfilled, onRejected。分别在promise状态变为fulfiled或rejected后执行,这里涉及到将回调注册进两个执行队列的操作,后文会讲到。
const PENDING = 'pending'
const FULFILLED = 'fulfiled'
const REJECTED = 'rejected'
class NewPromise {
constructor(handler) {
this.state = PENDING
this.value = undefined
this.successCallback = []
this.failureCallback = []
try {
handler(this.resolve.bind(this), this.reject.bind(this))
} catch (e) {
this.reject(e)
}
}
// resolve和reject方法
resolve(value) { ... }
reject(reason) { ... }
// then方法
then(onFulfilled, onRejected) { ... }
}
复制代码
结构中的每个部分是如何实现的呢?
Promise的执行器
执行器是我们初始化promise时候传入的回调,是我们操作promise的入口,所以执行器的实现不复杂,也就是将我们传入的回调执行一下。
class NewPromise {
...
handler(resolve.bind(this), reject.bind(this))
...
}
复制代码
实际上,执行器会接受两个回调,resolve和reject。它们真正起到改变promise状态的作用。
resolve和reject
实际上是两个函数,作为参数传入到执行器中,所做的事情不复杂:
改变promise的状态
将接收的值作为promise的value
依次执行then中注册的回调
const PENDING = 'pending'
const FULFILLED = 'fulfiled'
const REJECTED = 'rejected'
class NewPromise {
constructor(handler) {
this.state = PENDING
this.value = undefined
// 两个队列,后面会讲到
this.successCallback = []
this.failureCallback = []
try {
// 执行器,由于resolve和reject中用到了this,这里需要bind一下
handler(this.resolve.bind(this), this.reject.bind(this))
} catch (e) {
this.reject(e)
}
}
resolve(value) {
if (this.state !== PENDING) return
this.state = FULFILLED
this.value = value
// 用setTimeout模拟异步方式
setTimeout(() => {
this.successCallback.forEach(item => {
item(value)
})
})
}
reject(reason) {
if (this.state !== PENDING) return
this.state = REJECTED
this.value = reason
setTimeout(() => {
this.failureCallback.forEach(item => {
setTimeout(item(reason))
})
})
}
}
复制代码
看一下它们的实现,改变状态、赋值value。最重要的一点:循环执行then方法注册到队列中的回调。 而规范中要求回调以异步方式执行,这样保证在执行所有的回调之前,所有回调已经通过then注册完成,所以这里用setTimeout模拟了一下。
then方法:
(翻译整理自Promise/A+ 规范)
promise必须提供then方法来访问这个promise当前或者最终的值。 then方法有两个参数:onFulfilled, onRejected,都是可选的。关于这两个参数,这里有几个规则:
onFulfilled, onRejected都不是函数的时候,必须被忽略
实际上忽略的意思也就是如果不是函数,默认给它赋值成函数,返回值为then所属的promise的值。这样是做是为了在then()函数未传回调的时候,可以将promise的值传递下去。场景如下:
promise(resolve => resolve('success'))
.then()
.then(function(value) {
console.log(value)
})
复制代码
具体实现上,在它不是函数的时候可以给它赋值一个默认函数,也可以直接调用新返回的promise中的resolve或reject将值传下去,来达到忽略的效果。
onFulfilled是函数的时候
必须在当前的promise的状态变为fulfilled的时候被调用,promise被resolve的值也就是它的第一个参数
不能在fulfilled之前被调用
最多只能被调用一次
onRejected是函数的时候
必须在当前的promise的状态变为rejected的时候被调用,promise被reject的值也就是它的第一个参数。不能在rejected之前被调用,
最多只能被调用一次。
then可能会被调用多次
当promise状态变为fulfilled,所有onFulfilled将会按照最开始在then方法中注册的顺序去调用
当promise状态变为rejected,所有onRejected将会按照最开始在then方法中注册的顺序去调用,就像下边这样:
const promise = new Promise((resolve, reject) => {
setTimeout(() => resolve('success'))
})
promise.then(res => {
console.log(res, '第一次');
})
promise.then(res => {
console.log(res, '第二次');
})
复制代码
鉴于这