写过好几次promise,但是感觉还是没有完全掌握,今天又写了一遍,理下思路:
一、promise类
- 写一个promise类,包含以下状态:
- status
- value
- reason
- resolveQueue
- rejectQueue
- resolve的操作
- 更新状态为fulfilled
- 设置value的值
- 调用resolveQueue队列
- reject的操作
- 更新状态为reject
- 设置reason的值
- 调用rejectQueue队列
- then:最重要
- 值透传:onFulfilled、onReject不是必传的,如果没有传要写一个函数,resolve就是直接返回参数,reject是throw错误
- 返回一个新创建的promise,内部判断三种状态,如果是pending,则push进两个队列,resolve则执行fulfilled队列,reject则执行reject队列
- 具体的执行函数:执行onFulfilled或者onReject函数得到返回值,判断它的返回值和promise2的关系,进入resolvePromise的流程
/**
* 1. 包含状态值、value、reason、完成队列函数、拒绝队列函数
* 2. resolve的操作:更新状态为fulfilled,调用成功函数队列
* 3. reject的操作:更新状态为reject,调用失败队列
* 4. then
* 1. 值透传
* 2. 返回一个新创建的promise,内部判断状态,如果是pending,则push进去,resolve则执行fulfilled队列,reject则执行reject队列
* 3. 具体的执行函数,执行onFulfilled函数,判断它的返回值和promise2的关系,进入resolvePromise的流程
*/
class MyPromise2 {
static PENDING = 'pending';
static FULFILLED = 'fulfilled';
static REJECTED = 'reject';
constructor(executor) {
this.status = MyPromise2.PENDING;
this.resolveQueue = [];
this.rejectQueue = [];
this.value = null;
this.reason = null;
if (typeof executor !== 'function') {
throw new TypeError('Promise xx');
}
try {
executor(this.resolve.bind(this), this.reject.bind(this));
} catch(err) {
this.reject();
}
}
resolve(value) {
if (this.status !== MyPromise2.PENDING) {
return;
}
this.value = value;
this.status = MyPromise2.FULFILLED;
this.resolveQueue.forEach(item => {
item(this.value);
});
}
reject(reason) {
if (this.status !== MyPromise2.PENDING) {
return;
}
this.reason = reason;
this.status = MyPromise2.REJECTED;
this.rejectQueue.forEach(item => {
item(this.reason);
});
}
// 如果pending,则push进去,否则执行
then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
onRejected = typeof onRejected === 'function' ? onRejected : reason => {throw reason};
const promise2 = new MyPromise2((resolve, reject) => {
if (this.status === MyPromise2.PENDING) {
this.resolveQueue.push(() => {
setTimeout(() => {
try {
const x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (err) {
reject(err);
}
});
});
this.rejectQueue.push(() => {
setTimeout(() => {
try {
const x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (err) {
reject(err);
}
});
});
} else if (this.status === MyPromise2.FULFILLED) {
setTimeout(() => {
try {
const x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (err) {
reject(err);
}
});
} else if (this.status === MyPromise2.REJECTED) {
setTimeout(() => {
try {
const x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (err) {
reject(err);
}
});
}
});
return promise2;
}
}
二、resolvePromise函数
2.2.7 then方法必须返回一个promise。[3.3]
promise2 = promise1.then(onFulfilled, onRejected);
2.2.7.1. 如果onFulfilled或onRejected返回一个值x,则运行Promise Resolution Procedure [[Resolve]](promise2, x)。
2.2.7.2. 如果onFulfilled或onRejected抛出异常e,则promise2必须以e作为原因被拒绝。
下面resolvePromise的决议过程:
-
判断是否同一个promise,是的话直接抛出异常
-
如果x不为null,是对象或者函数
- 取出x.then
- 如果x.then是函数,则调用x.then, x作为context,传入resolve,reject函数,resolve时继续调用resolvePromise来对y和promise2做决议,直到y不是promise,就会走resolve流程了
- 不是函数,则直接resolve(x)
- 取出x.then
-
否则,直接resolve(x)
/**
* @param {*} promise2
* @param {*} x
* @param {*} resolve
* @param {*} reject
* @returns
*/
function resolvePromise(promise2, x, resolve, reject) {
// 2.3.1 如果`promise`和`x`引用同一个对象,则以`TypeError`为原因拒绝`promise`
if (x === promise2) {
return reject(new TypeError('Error'));
}
let called;
if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
try {
const then = x.then;
if (typeof then === 'function') {
then.call(x, y => {
if (called) {
return;
}
called = true;
// 2.3.3.3.1 如果/当`resolvePromise`被调用并传入值`y`,运行`[[Resolve]](promise, y)`。
resolvePromise(promise2, y, resolve, reject)
}, err => {
if (called) {
return;
}
called = true;
// 2.3.3.2 如果/当`rejectPromise`被调用并传入原因`r`,以`r`拒绝`promise`
reject(err);
})
} else {
resolve(x);
}
} catch (e) {
// 2.3.3.2 如果获取属性`x .then`导致抛出异常`e`,则以`e`为原因拒绝`promise`
if (called) {
return;
}
called = true;
reject(e);
}
} else {
// 2.3.3.4 如果`then`不是一个函数,则以`x`来实现`promise`
// 2.3.4 如果`x`不是对象或函数,则用`x`来实现`promise`
resolve(x);
}
}
测试代码:
// 自测promise
const promise = new MyPromise2((resolve, reject) => {
setTimeout(() => {
resolve('成功');
},1000);
}).then(
(data) => {
console.log('success', data)
},
(err) => {
console.log('faild', err)
}
)
全部代码已上传github。
三、运行测试用例
通过(promises-aplus-tests)可以帮助我们测试所编写的代码是否符合 Promise/A+ 的规范。需要安装npm包。同时运行测试需要添加如下代码:
// 执行promises-aplus-tests测试用例需要用到的代码
MyPromise2.deferred = function() {
let defer = {};
defer.promise = new MyPromise2((resolve, reject) => {
defer.resolve = resolve;
defer.reject = reject;
});
return defer;
}
安装测试脚本
npm i -g promises-aplus-tests
开始测试
promises-aplus-tests MyPromise.js