序言
Promise
是ES6提出的异步编程规范。接下来我们一步一步来探究下Promise的实现原理,根据Promise A+规范实现一个我们自己的Promise。
Promise的初步实现
Promise的简单使用
new Promise((resolve, reject) => {
resolve('a')
}).then(value => {
console.log(value)
}, reason => {
console.log(reason)
})
Promise有三种状态: 等待态(Pending)、执行态(Fulfilled)和拒绝态(Rejected)。
- 等待态(Pending)
处于等待态时,promise 需满足以下条件:- 可以迁移至执行态或拒绝态
- 执行态(Fulfilled)
处于执行态时,promise 需满足以下条件:- 不能迁移至其他任何状态
- 必须拥有一个不可变的终值
- 拒绝态(Rejected)
处于拒绝态时,promise 需满足以下条件:- 不能迁移至其他任何状态
- 必须拥有一个不可变的据因
同时promise 必须提供一个 then 方法以访问其当前值、终值和据因。
promise 的 then 方法接受两个参数:
promise.then(onFulfilled, onRejected)
onFufilled是promise状态为Fulfilled
时的回调,onRejected
是promise状态为Rejected
时的回调。
根据以上的基本要求,我们可以实现一个得到Promise的简单实现
const PENDING = 'pending'
const FULLFILLED = 'fullfilled'
const REJECTED = 'rejected'
class MyPromise {
constructor(executor) {
// excutor必须是函数
if (typeof executor !== 'function') {
throw new TypeError('Promise resolver a is not a function')
}
// 状态
this.status = PENDING
// fullfilled时接受的参数
this.value = null
// rejected的原因
this.reason = null
this.fullfilledCallback = []
this.rejectCallback = []
try {
executor(this.resolve.bind(this), this.reject.bind(this))
} catch (error) {
this.reject(error)
}
}
resolve(value) {
if (this.status === PENDING) {
this.value = value
this.status = FULLFILLED
}
}
reject(reason) {
if (this.status === PENDING) {
this.reason = reason
this.status = REJECTED
}
}
then(onFullfilled, onRejected) {
// 如果onFullfilled和onRejected不是函数,则将其包装成函数
if (typeof onFullfilled !== 'function') {
onFulfilled = function (value) {
return value
}
}
if (typeof onRejected !== 'function') {
onRejected = function(reason) {
throw reason
}
}
if (this.status === FULLFILLED) {
onFulfilled(this.value)
}
if (this.status === REJECTED) {
onRejected(this.reason)
}
}
}
Promise异步解决方案实现
我们知道Promise时的then中的回调函数时异步执行的,比如下面这段代码
console.log(1)
new Promise((resolve, reject) => {
console.log(2)
resolve('a')
}).then(value => {
console.log(3)
console.log(value)
}, reason => {
console.log(reason)
})
console.log(4)
输入为1, 2, 4, 3
,所以我们需要将then
中的回调变为异步执行
then(onFullfilled, onRejected) {
// 如果onFullfilled和onRejected不是函数,则将其包装成函数
if (typeof onFullfilled !== 'function') {
onFulfilled = function (value) {
return value
}
}
if (typeof onRejected !== 'function') {
onRejected = function(reason) {
throw reason
}
}
if (this.status === FULLFILLED) {
setTimeout(() => {
onFulfilled(this.value)
}, 0);
}
if (this.status === REJECTED) {
setTimeout(() => {
onRejected(this.reason)
}, 0);
}
}
在这里还有一个问题,如果我们传入的executor
有异步逻辑怎么办,比如
console.log(1)
new MyPromise((resolve, reject) => {
console.log(2)
setTimeout(() => {
resolve('haha')
})
}).then(value => {
console.log(3)
console.log(value)
}, reason => {
console.log(reason)
})
console.log(4)
按照刚刚完成的代码,then
方法的执行早于resolve
方法的执行。在这种情况下,then
方法执行时,Promise的状态为pending
。这时候,我们需要将onFulfiiled
和onReject
函数保存下来,当Promise状态改变为fulfilled
或者rejected
时再调用回调函数,所以
const PENDING = 'pending'
const FULLFILLED = 'fullfilled'
const REJECTED = 'rejected'
class MyPromise {
constructor(executor) {
// excutor必须是函数
if (typeof executor !== 'function') {
throw new TypeError('Promise resolver a is not a function')
}
// 状态
this.status = PENDING
// fullfilled时接受的参数
this.value = null
// rejected的原因
this.reason = null
this.fullfilledCallback = []
this.rejectCallback = []
try {
executor(this.resolve.bind(this), this.reject.bind(this))
} catch (error) {
// 为了能捕获
this.reject(error)
}
}
resolve(value) {
if (this.status === PENDING) {
this.value = value
this.status = FULLFILLED
this.fullfilledCallback.forEach(fn => {
fn(value)
})
}
}
reject(reason) {
if (this.status === PENDING) {
this.reason = reason
this.status = REJECTED
this.rejectCallback.forEach(fn => {
fn(reason)
})
}
}
then(onFullfilled, onRejected) {
// 如果onFullfilled和onRejected不是函数,则将其包装成函数
if (typeof onFullfilled !== 'function') {
onFulfilled = function (value) {
return value
}
}
if (typeof onRejected !== 'function') {
onRejected = function(reason) {
throw reason
}
}
if (this.status === FULLFILLED) {
setTimeout(() => {
onFulfilled(this.value)
}, 0);
}
if (this.status === REJECTED) {
setTimeout(() => {
onRejected(this.reason)
}, 0);
}
if (this.status === PENDING) {
this.fullfilledCallback.push(value => {
setTimeout(() => {
const x = onFulfilled(this.value)
})
})
this.rejectCallback.push(reason => {
setTimeout(() => {
onRejected(this.reason)
})
})
}
}
}
Promise 链式调用的实现
Promise then
方法必须返回了以新的Promise,这样就同时实现了Promise的链式调用,既然返回一个新的Promise的,那么我们在then
方法中关于状态判断的执行逻辑放哪里?简单:只需放在新创建的Promise的executor
函数中就行
then(onFullfilled, onRejected) {
// 如果onFullfilled和onRejected不是函数,则将其包装成函数
if (typeof onFullfilled !== 'function') {
onFulfilled = function (value) {
return value
}
}
if (typeof onRejected !== 'function') {
onRejected = function(reason) {
throw reason
}
}
let promise2 = new MyPromise((resolve, reject) => {
if (this.status === FULLFILLED) {
setTimeout(() => {
try {
const x = onFulfilled(this.value)
resolve(x)
} catch (error) {
reject(error)
}
}, 0);
}
if (this.status === REJECTED) {
setTimeout(() => {
try {
const x = onRejected(this.reason)
resolve(x)
} catch (e) {
reject(e)
}
}, 0);
}
if (this.status === PENDING) {
this.fullfilledCallback.push(value => {
setTimeout(() => {
try {
const x = onFulfilled(this.value)
resolve(x)
} catch (error) {
reject(error)
}
})
})
this.rejectCallback.push(reason => {
setTimeout(() => {
try {
const x = onRejected(this.reason)
resolve(x)
} catch (e) {
reject(e)
}
})
})
}
})
return promise2
}
在这里又有了一个问题,我们的Promise,如果onFulfiilled的返回值是基本数据类型是可以正常链式调用的。但如果onFulfilled
返回一个新的Promise会怎样呢。我们可以使用下面的代码用ES6
的Promise和我们自己的Promise测试以下
new Promise((resolve, reject) => {
resolve('haha')
}).then(value => {
console.log(value)
return new Promise(() => {
resolve("hehe")
})
}, reason => {
console.log(reason)
}).then(val => {
console.log(val)
})
从测试结果我们可以很容易的看出我们的Promise
是不符合规范的。该怎么办呢?
Promise 链式调用的最终解决方法
Promise A+ 对onFulfilled
和onRejected
的返回值x
的处理做了明确的规定:
如果 onFulfilled 或者 onRejected 返回一个值 x ,则运行下面的 Promise 解决过程:
Promise解决过程:
Promise 解决过程是一个抽象的操作,其需输入一个 promise 和一个值,我们表示为 [[Resolve]](promise, x),如果 x 有 then 方法且看上去像一个 Promise ,解决程序即尝试使 promise 接受 x 的状态;否则其用 x 的值来执行 promise 。
这种 thenable 的特性使得 Promise 的实现更具有通用性:只要其暴露出一个遵循 Promise/A+ 协议的 then 方法即可;这同时也使遵循 Promise/A+ 规范的实现可以与那些不太规范但可用的实现能良好共存。
运行 [[Resolve]](promise, x) 需遵循以下步骤:
-
x 与 promise 相等
如果 promise 和 x 指向同一对象,以 TypeError 为据因拒绝执行 promise -
x 为 Promise
如果 x 为 Promise ,则使 promise 接受 x 的状态:- 如果 x 处于等待态, promise 需保持为等待态直至 x 被执行或拒绝
- 如果 x 处于执行态,用相同的值执行 promise
- 如果 x 处于拒绝态,用相同的据因拒绝 promise
-
x 为对象或函数
如果 x 为对象或者函数:- 把 x.then 赋值给 then 注5
- 如果取 x.then 的值时抛出错误 e ,则以 e 为据因拒绝 promise
- 如果 then 是函数,将 x 作为函数的作用域 this 调用之。传递两个回调函数作为参数,第一个参数叫做 resolvePromise ,第二个参数叫做 rejectPromise:
- 如果 resolvePromise 以值 y 为参数被调用,则运行 [[Resolve]](promise, y)
- 如果 rejectPromise 以据因 r 为参数被调用,则以据因 r 拒绝 promise
- 如果 resolvePromise 和 rejectPromise 均被调用,或者被同一参数调用了多次,则优先采用首次调用并忽略剩下的调用
- 如果调用 then 方法抛出了异常 e:
- 如果 resolvePromise 或 rejectPromise 已经被调用,则忽略之
- 否则以 e 为据因拒绝 promise
- 如果 then 不是函数,以 x 为参数执行 promise
-
如果 x 不为对象或者函数,以 x 为参数执行 promise
本质上Promise解决方法就是一个函数,用来处理onFulfilled
或者onRejected
的返回值x
,下面我们来实现这个函数
MyPromise.resovePromise = (x, promise, resovle, reject) => {
// 如果 promise 和 x 指向同一对象,以 TypeError 为据因拒绝执行 promise
if (x === promise) {
reject(new TypeError("Chaining cycle detected for promise #<Promise>"))
}
// 如果x是promise,等待x执行完毕
if (x instanceof MyPromise) {
// 等待x执行完毕
x.then(value=> {
// value 也有可能是一个Promise或者对象或者函数
MyPromise.resovePromise(value, promise, resovle, reject)
}, reason => {
reject(reason)
})
} else if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
const then = x.then
// 如果x拥有then是个函数
// 如果 resolvePromise 和 rejectPromise 均被调用,或者被同一参数调用了多次,则优先采用首次调用并忽略剩下的调用, 所以我们表明一个变量是否被调用过
let call = false
try {
if (typeof then === 'function') {
then.call(x, value => {
if (call) return
call = true
MyPromise.resovePromise(value, promise, resovle, reject)
}, reason => {
if (call) return
call = true
reject(reason)
})
} else {
if (call) return
call = true
resolve(x)
}
} catch(e) {
if (call) return
call = true
reject(e)
}
} else {
try {
resolve(x)
} catch (e) {
reject(e)
}
}
}
之后我们对onFulfiiled和onReject的返回值应用这个函数就可以
let promise2 = new MyPromise((resolve, reject) => {
if (this.status === FULLFILLED) {
setTimeout(() => {
try {
const x = onFulfilled(this.value)
MyPromise.resovePromise(x, promise2, resolve, reject)
} catch (error) {
reject(error)
}
}, 0);
}
if (this.status === REJECTED) {
setTimeout(() => {
try {
const x = onRejected(this.reason)
MyPromise.resovePromise(x, promise2, resolve, reject)
} catch (e) {
reject(e)
}
}, 0);
}
if (this.status === PENDING) {
this.fullfilledCallback.push(value => {
setTimeout(() => {
try {
const x = onFulfilled(this.value)
MyPromise.resovePromise(x, promise2, resolve, reject)
} catch (error) {
reject(error)
}
})
})
this.rejectCallback.push(reason => {
setTimeout(() => {
try {
const x = onRejected(this.reason)
MyPromise.resovePromise(x, promise2, resolve, reject)
} catch (e) {
reject(e)
}
})
})
}
})
至此,我们的Promise就完成了
完成代码
const PENDING = 'pending'
const FULLFILLED = 'fullfilled'
const REJECTED = 'rejected'
class MyPromise {
constructor(executor) {
// excutor必须是函数
if (typeof executor !== 'function') {
throw new TypeError('Promise resolver a is not a function')
}
// 状态
this.status = PENDING
// fullfilled时接受的参数
this.value = null
// rejected的原因
this.reason = null
this.fullfilledCallback = []
this.rejectCallback = []
try {
executor(this.resolve.bind(this), this.reject.bind(this))
} catch (error) {
// 为了能捕获
this.reject(error)
}
}
resolve(value) {
if (this.status === PENDING) {
this.value = value
this.status = FULLFILLED
this.fullfilledCallback.forEach(fn => {
fn(value)
})
}
}
reject(reason) {
if (this.status === PENDING) {
this.reason = reason
this.status = REJECTED
this.rejectCallback.forEach(fn => {
fn(reason)
})
}
}
then(onFullfilled, onRejected) {
// 如果onFullfilled和onRejected不是函数,则将其包装成函数
if (typeof onFullfilled !== 'function') {
onFulfilled = function (value) {
return value
}
}
if (typeof onRejected !== 'function') {
onRejected = function(reason) {
throw reason
}
}
let promise2 = new MyPromise((resolve, reject) => {
if (this.status === FULLFILLED) {
setTimeout(() => {
try {
const x = onFulfilled(this.value)
MyPromise.resovePromise(x, promise2, resolve, reject)
} catch (error) {
reject(error)
}
}, 0);
}
if (this.status === REJECTED) {
setTimeout(() => {
try {
const x = onRejected(this.reason)
MyPromise.resovePromise(x, promise2, resolve, reject)
} catch (e) {
reject(e)
}
}, 0);
}
if (this.status === PENDING) {
this.fullfilledCallback.push(value => {
setTimeout(() => {
try {
const x = onFulfilled(this.value)
MyPromise.resovePromise(x, promise2, resolve, reject)
} catch (error) {
reject(error)
}
})
})
this.rejectCallback.push(reason => {
setTimeout(() => {
try {
const x = onRejected(this.reason)
MyPromise.resovePromise(x, promise2, resolve, reject)
} catch (e) {
reject(e)
}
})
})
}
})
return promise2
}
}
MyPromise.resovePromise = (x, promise, resovle, reject) => {
// 如果 promise 和 x 指向同一对象,以 TypeError 为据因拒绝执行 promise
if (x === promise) {
reject(new TypeError("Chaining cycle detected for promise #<Promise>"))
}
// 如果x是promise,等待x执行完毕
if (x instanceof MyPromise) {
// 等待x执行完毕
x.then(value=> {
// value 也有可能是一个Promise或者对象或者函数
MyPromise.resovePromise(value, promise, resovle, reject)
}, reason => {
reject(reason)
})
} else if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
const then = x.then
// 如果x拥有then是个函数
// 如果 resolvePromise 和 rejectPromise 均被调用,或者被同一参数调用了多次,则优先采用首次调用并忽略剩下的调用, 所以我们表明一个变量是否被调用过
let call = false
try {
if (typeof then === 'function') {
then.call(x, value => {
if (call) return
call = true
MyPromise.resovePromise(value, promise, resovle, reject)
}, reason => {
if (call) return
call = true
reject(reason)
})
} else {
if (call) return
call = true
resolve(x)
}
} catch(e) {
if (call) return
call = true
reject(e)
}
} else {
try {
resolve(x)
} catch (e) {
reject(e)
}
}
}