Appearance
js
// 判断当前then方法中传入的参数是否是函数
const isFunction = (fn) => typeof fn === 'function'
class MyPromise {
// 构造器
constructor(executor) {
// 设置Promise实例对象的状态,默认pending状态;
this.state = 'pending'
// 设置Promise实例对象成功回调函数的参数,默认undefined;
this.value = undefined
// 设置Promise实例对象失败回调函数的参数,默认undefined;
this.reason = undefined
// 设置异步操作成功的回调函数
this.resolvedPromiseCallbacks = []
// 设置异步操作失败的回调函数
this.rejectedPromiseCallbacks = []
// 设置成功回调函数
let resolve = (val) => {
// 成功回调函数控制Promise实例对象成功的状态和参数
if (this.state === 'pending') {
this.state = 'fulfilled'
this.value = val
// 执行异步操作成功的回调函数
this.resolvedPromiseCallbacks.forEach((fn) => fn())
}
}
// 设置失败回调函数
let reject = (reason) => {
// 失败回调函数控制Promise实例对象失败的状态的参数
if (this.state === 'pending') {
this.state = 'rejected'
this.reason = reason
// 执行异步操作失败的回调函数
this.rejectedPromiseCallbacks.forEach((fn) => fn())
}
}
// executor执行者函数执行
try {
// 1. try...catch语句包裹因为executor函数执行可能存在异常,所以需要捕获
// 2. resolove, reject是形式参数还是实际参数,因为executor是作为实际参数进行传递的,而resolve,reject是形式参数,那么形式参数要与实际参数做对应;
executor(resolve, reject)
} catch (err) {
reject(err)
}
}
// Promise.prototype的then方法,存在成功的回调,失败的回调
then(onFulfilled, onRejected) {
onFulfilled = isFunction(onFulfilled) ? onFulfilled : (data) => data
onRejected = isFunction(onRejected)
? onRejected
: (err) => {
throw err
}
// 1.then()方法的链式调用的入口在哪呢?有人可能认为then方法返回this即可,但是返回this,此时当前的this指代的上一个Promise实例对象,而下一个then方法与上一个Promise实例对象并没有关系,所以每次调用then方法都会返回一个全新的Promise实例对象;
let p2 = new MyPromise((resolve, reject) => {
// 因为p2实例对象中的executor是同步执行,所以可以将下面的代码放入executor中,调用then方法时直接初始化;
let x
// 2.链式调用的关键在哪呢?也就是说上一次then方法中的成功或者失败回调函数的返回值是下一次then方法成功或者失败回调函数的参数;(针对原始值)
// 根据Promise实例对象的状态进行调用不同的回调函数
if (this.state === 'fulfilled') {
setTimeout(() => {
// 因为我们知道resolvePromise可能出现失败情况,所以要try...catch
try {
x = onFulfilled(this.value)
// Promise链式调用代码操作(成功or失败函数返回引用值)的入口在哪呢?就是上一次成功or失败函数返回值的判断;判断x是否是原始值还是引用值;
resolvePromise(p2, x, resolve, reject)
} catch (err) {
reject(err)
}
}, 0)
// p2的成功回调函数
// resolve(x);
}
if (this.state === 'rejected') {
setTimeout(() => {
try {
x = onRejected(this.reason)
// p2的成功回调函数,为什么调用resolve呢?我们知道当失败回调函数返回原始值的话,那么会执行下一个then方法的成功回调(针对返回原始值);
// resolve(x);
resolvePromise(p2, x, resolve, reject)
} catch (err) {
reject(err)
}
})
}
// Promise异步操作的入口在哪呢?setTimeout是同步接口,会在WebAPIS中注册回调函数(异步),而p1.then()是同步操作,此时p1的状态必须等到回调函数执行才能够确定,所以此时p1的状态是pending,所以Promise实例对象还存在一个pending状态;
if (this.state === 'pending') {
// 那通过哪种方式可以确定什么时间执行回调函数呢?利用发布订阅和装饰器的思想,先将回调函数保存,等到Promise实例对象状态确定之后再去执行;
// 为什么不直接这样操作呢?因为如果直接保存成功或者失败的回调函数,此时回调函数的参数会丢失;
// this.rejectedPromiseCallbacks.push(onFulfilled);
this.resolvedPromiseCallbacks.push(() => {
setTimeout(() => {
try {
x = onFulfilled(this.value)
resolvePromise(p2, x, resolve, reject)
} catch (err) {
reject(err)
}
})
})
this.rejectedPromiseCallbacks.push(() => {
setTimeout(() => {
try {
x = onRejected(this.reason)
resolvePromise(p2, x, resolve, reject)
} catch (err) {
reject(err)
}
})
})
}
})
return p2
}
}
function resolvePromise(p, x, resolve, reject) {
// 此时存在三个问题
// 1. 根据PromiseA+的规范,如果回调函数返回引用值,此时then方法返回的Promise不能与回调函数返回的Promise实例相同;
// 2. 此时获取不到P2实例对象,因为P2作为Promise实例对象,此时构造函数并未执行完成,而在内部获取P2肯定是获取不到的,又因为P2被let声明,所以此时产生暂时性死区的问题;---> 通过setTimeout进行解决;
// 3. 返回值x需不需要设置延迟的问题,如果不给返回值设置延时,此时p2的executor函数是同步执行,此时x返回值会立即出结果,与全局同步代码执行发生bug,所以也需要定时执行;
// 为了让每一次then方法中的回调函数执行一次,所以可以设置一把锁;
let called
if (p === x) {
return reject(new TypeError('Type Error'))
}
// 判断当前返回值x是Promise对象、thenable对象、普通对象、普通值
if ((typeof x === 'object' && x !== null) || typeof x === 'function') {
// 进一步判断对象的类型
// 获取对象的then方法,此时then方法不存在,抛出异常,进行捕获
try {
let then = x.then
if (typeof then === 'function') {
// Promise对象,或者是thenable对象,那么执行then方法
// 改变返回值promise对象回调函数,所以需要改变当前then方法中的this指向;
then.call(
x,
(val) => {
if (called) return
called = true
// resolve(val);
resolvePromise(p, val, resolve, reject)
},
(err) => {
if (called) return
called = true
reject(err)
}
)
} else {
if (called) return
called = true
// 普通对象,执行p2成功的回调函数
resolve(x)
}
} catch (err) {
if (called) return
called = true
reject(err)
}
} else {
if (called) return
called = true
// 普通值的话,就执行p2成功的回调函数
resolve(x)
}
}
MyPromise.defer = MyPromise.deferred = function () {
var dfd = {}
dfd.promise = new Promise((resolve, reject) => {
dfd.resolve = resolve
dfd.reject = reject
})
return dfd
}
module.exports = MyPromise