Promise A+规范的实现
source link: https://icodex.me/2022/01/02/Promise%20A+%E8%A7%84%E8%8C%83%E7%9A%84%E5%AE%9E%E7%8E%B0/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
Promise A+规范的实现
Promise/A+
Promise 对象
- 一个对象或者函数,并且带有
then
方法 - 一个当前状态
state
,可以是pending
、fulfilled
和rejected
,且fulfilled
和rejected
不可被改变 - 一个
resolve
值value
,可以是任何 JS 值的形式 - 一个执行
reject
的原因reason
then 方法
newPromise = promise.then(onFulfilled, onRejected)
then
方法接收两个参数,且必须都是函数,否则忽略该参数
onFulfilled
:当前 Promise 对象的状态变成fulfilled
以后执行,并且将value
作为第一个参数,并且只允许执行一次onRejected
:当前 Promise 对象的状态变成rejected
以后执行,并且将reason
作为第一个参数,并且只允许执行一次onFulfilled
和onRejected
必须在当前宏任务执行完以后才能执行,也就是异步的需求then
执行完返回一个新的 Promise 对象newPromise
:- 如果
onFulfilled
或者onRejected
返回新的值x
,则执行下文的方法Resolve(newPromise, x)
- 如果
onFulfilled
或者onRejected
执行抛出异常e
,promise2
也必须reject(e)
- 如果
onFulfilled
不是函数(未提供),则当promise
状态变成fulfilled
的时候,newPromise
内部状态也要变成fulfilled
并以promise
内部的value
执行resolve
;同理onRejected
也是
- 如果
- 同一个 Promise 对象的
then
方法可能调用多次- 并且各自
onFulfilled
或者onRejected
回调必须按照调用then
的顺序依次执行
- 并且各自
Resolve(promise, x)
Resolve(promise, x)
这里是描述then
执行以后的算法判断,也就是then
的反复执行过程,这是一个特殊的内部方法,是处理then
链式执行的算法;
- 如果
promise === x
,直接reject promise
并返回TypeError
的错误; - 如果
x
是一个 Promise 对象,则当前 Promise 对象promise
的状态必须和x
同步; - 如果
x
是一个对象或者函数:- 判断其是否具有
then
方法; - 如果没有,则
reject
; - 如果具有
then
,则使用x
作为then
内部的this
执行then
,第一个参数传递resolvePromise
,第二个参数传递rejectPromise
- 当
resolvePromise
被传递参数y
调用的时候,执行Resolve(promise, y)
- 当
rejectPromise
被传递参数r
调用的时候,则promise
也reject
- 如果
resolvePromise
和rejectPromise
都被调用了,或者被多次调用,首次调用优先执行,后续被忽略; - 如果执行
then
抛出异常,当resolvePromise
或者rejectPromise
被调用了,则忽略;否则reject
顶层的promise
- 当
- 判断其是否具有
- 如果
x
不满足上述条件,promise
直接fulfilled
实现
如果按照 promise-aplus 的算法慢慢摸索就可以直接实现一个完整的 Promise
初始化
Promise 对象在初始化以后,具有以上提到的value
,state
以及reason
等状态值
class APromise {
constructor(fn) {
if (typeof fn !== 'function') {
throw new TypeError('init parameter must be a function');
}
this.state = 'pending';
this.value = null;
this.reason = null;
try {
fn(this.resolve, this.reject);
} catch (e) {
this.reject(e);
}
}
resolve(value) {
setTimeout(() => {
if (this.state === 'pending') {
this.state = 'fulfilled';
this.value = value;
}
});
}
reject(reason) {
setTimeout(() => {
if (this.state === 'pending') {
this.state = 'rejected';
this.reason = reason;
}
});
}
}
then
在上述代码的基础上,添加then
方法,因为then
方法可能被调用多次,所以我们需要额外定义两个队列保存在 Promise 状态发生变化以后的回调函数onFulfilled
或onRejected
,这里做一个参数非函数类型的简单化处理,这样只需要考虑函数的回调形式,毕竟日常开发也不会传递其他类型的参数
class APromise {
constructor(fn) {
this.state = 'pending';
this.value = null;
this.reason = null;
this.fulfilledQue = [];
this.rejectedQue = [];
fn(this.resolve, this.reject);
}
then = (onFulfilled, onRejected) => {
this.fulfilledQue.push(
typeof onFulfilled === 'function' ? onFulfilled : value => value,
);
this.rejectedQue.push(
typeof onRejected === 'function'
? onRejected
: reason => {
throw reason;
},
);
};
}
然后then
方法返回一个新的 Promise 对象newPromise
,并且这个新的 Promise 对象和当前 Promise 的状态保持同步,所以在then
内部需要判断当前 Promise 的状态来进行不同的处理:
- 如果是
fulfilled
状态,则表明当前 Promise 已经执行了resolve
,所以应该以当前 Promise 的value
异步执行onFulfilled
,并把当前 Promise 的value
传递下去;rejected
状态同理 - 而如果当前 Promise 状态是
pending
,那么应该将onFulfilled
或onRejected
推到回调函数中去,等待状态改变的时候自动调用
then = (onFulfilled, onRejected) => {
onFulfilled =
typeof onFulfilled === 'function' ? onFulfilled : value => value;
onRejected =
typeof onRejected === 'function'
? onRejected
: reason => {
throw reason;
};
const newPromise = new APromise((resolve, reject) => {
if (this.state === 'fulfilled') {
// https://promisesaplus.com/#point-43
// 这里直接异步执行回调函数,保证返回的 Promise 状态和当前 Promise 状态一致
setTimeout(() => {
try {
const x = onFulfilled(this.value);
this.resolvePromise(newPromise, x, resolve, reject);
} catch (e) {
reject(e);
}
});
}
if (this.state === 'rejected') {
setTimeout(() => {
try {
const x = onRejected(this.reason);
this.resolvePromise(newPromise, x, resolve, reject);
} catch (e) {
reject(e);
}
});
}
// 如果当前 Promise 状态
if (this.state === 'pending') {
this.fulfilledQue.push(value => {
try {
const x = onFulfilled(value);
this.resolvePromise(newPromise, x, resolve, reject);
} catch (e) {
reject(e);
}
});
this.rejectedQue.push(reason => {
try {
const x = onRejected(reason);
this.resolvePromise(newPromise, x, resolve, reject);
} catch (e) {
reject(e);
}
});
}
});
return newPromise;
};
然后我们需要在 Promise 状态发生改变的时候调用onFulfilled
或onRejected
,可以在原来的resolve
和reject
基础上进行补充,这里使用setTimeout
模拟异步实现的方式
resolve = value => {
setTimeout(() => {
if (this.state === 'pending') {
this.state = 'fulfilled';
this.value = value;
this.fulfilledQue.forEach(cb => {
cb(this.value);
});
}
});
};
reject = reason => {
setTimeout(() => {
if (this.state === 'pending') {
this.state = 'rejected';
this.reason = reason;
this.rejectedQue.forEach(cb => {
cb(this.reason);
});
}
});
};
resolvePromise
resolvePromise
的实现相对简单一点,直接按照规范定义一步一步来即可
resolvePromise = (promise, x, resolve, reject) => {
// https://promisesaplus.com/#point-48
// 如果then提供的onFulfilled或者onRejected函数执行返回的值和then返回的是同一个promise,会导致下文this.resolvePromise重复调用,形成死循环
if (promise === x) {
reject(
new TypeError(
'then must return a different promise with fulfilled callback',
),
);
}
// https://promisesaplus.com/#point-49
// 如果是一个 Promise,需要判断其状态,并同步到后续的 Promise
if (x instanceof APromise) {
// https://promisesaplus.com/#point-50
if (x.state === 'pending') {
x.then(
value => {
this.resolvePromise(promise, value, resolve, reject);
},
reason => {
reject(reason);
},
);
} else {
// https://promisesaplus.com/#point-51
x.then(resolve, reject);
}
} else if (typeof x === 'function' || (typeof x === 'object' && x !== null)) {
// https://promisesaplus.com/#point-53
// thenable 函数
let then;
// https://promisesaplus.com/#point-59
// 保证x.then提供的回调函数只会被执行一次
let hasBeenResolved = false;
try {
then = x.then;
// https://promisesaplus.com/#point-56
if (typeof then === 'function') {
then.call(
x,
y => {
if (!hasBeenResolved) {
hasBeenResolved = true;
// https://promisesaplus.com/#point-57
this.resolvePromise(promise, y, resolve, reject);
}
},
r => {
if (!hasBeenResolved) {
hasBeenResolved = true;
reject(r);
}
},
);
} else {
// https://promisesaplus.com/#point-63
resolve(x);
}
} catch (e) {
// https://promisesaplus.com/#point-60
if (!hasBeenResolved) {
reject(e);
}
}
} else {
// https://promisesaplus.com/#point-64
resolve(x);
}
};
测试
使用 promise-aplus 规范提供的promises-tests进行测试,在原有代码上暴露以下入口方法,然后使用 cjs 语法导出即可
/**
* 测试入口
*/
APromise.defer = APromise.deferred = function() {
let dfd = {};
dfd.promise = new APromise((resolve, reject) => {
dfd.resolve = resolve;
dfd.reject = reject;
});
return dfd;
};
执行promises-aplus-tests
,可以看到完美通过 872 个测试用例。
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK