JS手撕(八) Promise
Promise实现
Promise
的原理之前有写过两篇博客,就不细讲了。
但还是需要简单复习一下下。
Promise构造函数的实现
promise
的状态一开始是pending
,只能从pending
变为resolved
或从pending
变为rejected
。并且是不可逆的。
改变promise
的状态有三种方法,
- 调用 resolve 函数:pending => fulfilled(resolved)
- 调用 reject 函数:pending => rejected
- 抛出异常:pending => rejected
// 1. 初始状态:pending
const p1 = new Promise((resolve, reject) => { });
// 2. 调用resolve函数:pending => fulfilled(resolved)
const p2 = new Promise((resolve, reject) => resolve(123));
// 3. 调用reject函数:pending => rejected
const p3 = new Promise((resolve, reject) => reject('error'));
// 4. 抛出异常:pending => rejected
const p4 = new Promise((resolve, reject) => {
throw new Error('异常');
});
console.log(p1);
console.log(p2);
console.log(p3);
console.log(p4);
从上图可以看出,promise
对象还会有PromiseState
和PromiseResult
属性。
PromiseState
:对象的状态PromiseResult
:对象结果值
class MyPromise {
// Promise对象的状态
PromiseState = 'pending';
// Promise对象的结果
PromiseResult = undefined;
constructor(executor) { // 执行器函数executor
// 需要有`resolve`和`reject`函数,因为实例化Promise对象时需要传参`(resolve, reject) => { }`形式的函数
const resolve = (data) => {
if (this.PromiseState === 'pending') {
// 只能从`pending`状态变为`resolved`
this.PromiseState = 'fulfilled';
this.PromiseResult = data;
}
};
const reject = (data) => {
if (this.PromiseState === 'pending') {
// 只能从`pending`状态变为`rejected`
this.PromiseState = 'rejected';
this.PromiseResult = data;
}
};
executor(resolve, reject);
}
}
前3种情况已经能够实现了
代码语言:javascript复制// 1. 初始状态:pending
const p1 = new MyPromise((resolve, reject) => { });
// 2. 调用resolve函数:pending => fulfilled(resolved)
const p2 = new MyPromise((resolve, reject) => resolve(123));
// 3. 调用reject函数:pending => rejected
const p3 = new MyPromise((resolve, reject) => reject('error'));
console.log(p1);
console.log(p2);
console.log(p3);
但是,抛出异常的情况还不能实现,因为异常没有被捕获,所以会直接报错,后面的代码也不能执行。所以要我们在调用执行器函数executor
时,应该添加try catch
,如果被捕获,那就要执行reject
方法。
try {
executor(resolve, reject);
} catch (err) {
reject(err);
}
then方法的实现
首先,简单的实现一下
代码语言:javascript复制then(onResolved, onRejected) {
if (this.PromiseState === "fulfilled") {
onResolved(this.PromiseResult);
} else if (this.PromiseState === "rejected") {
onRejected(this.PromiseResult);
}
}
代码语言:javascript复制// 1. 初始状态:pending
const p1 = new MyPromise((resolve, reject) => { });
// 2. 调用resolve函数:pending => fulfilled(resolved)
const p2 = new MyPromise((resolve, reject) => resolve(123));
// 3. 调用reject函数:pending => rejected
const p3 = new MyPromise((resolve, reject) => reject('error'));
// 4. 抛出异常:pending => rejected
const p4 = new MyPromise((resolve, reject) => {
throw new Error('异常');
});
p1.then((data) => {
console.log(data);
});
p2.then((data) => {
console.log(data);
});
p3.then(null, (reason) => {
console.error(reason);
});
p4.then(null, (reason) => {
console.error(reason);
});
完美(×),使用Promise
难免遇到异步任务,所以还需要测试一下。
const p5 = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('123456');
}, 0)
})
p5.then((data) => {
console.log(data);
})
没反应。这是因为因为是异步任务,所以执行then
方法时,状态还是pending
,所以还需要定义数据结构来存回调函数,如果是异步任务,即状态是pending
时,存好回调函数。
then(onResolved, onRejected) {
if (this.PromiseState === "fulfilled") {
onResolved(this.PromiseResult);
} else if (this.PromiseState === "rejected") {
onRejected(this.PromiseResult);
} else if (this.PromiseState === 'pending') {
this.resolvedQueue.push(onResolved);
this.rejectedQueue.push(onRejected);
}
}
存好回调函数,自然还是需要调用的。所以resolve
函数和reject
函数最后还需要遍历执行对应回调队列。
const resolve = (data) => {
if (this.PromiseState === 'pending') {
// 只能从`pending`状态变为`resolved`
this.PromiseState = 'fulfilled';
this.PromiseResult = data;
this.resolvedQueue.forEach(callback => callback(data));
}
};
reject
函数同理
这样子就能处理异步任务了。
但是,这个时候Promise.then()
方法并不是异步任务。
const p5 = new MyPromise((resolve, reject) => {
resolve('123456');
console.log(88888);
})
p5.then((res) => {
console.log(res)
})
console.log(99999);
根据JavaScript的执行机制,应该先执行完同步任务,再执行异步任务,并且Promise.then()
是异步任务(微任务),所以应该依次输出88888
、99999
、123456
才对。
所以,then
方法还得改一下,需要添加定时器来让它变成异步任务。
then(onResolved, onRejected) {
if (this.PromiseState === "fulfilled") {
setTimeout(() => {
onResolved(this.PromiseResult);
})
} else if (this.PromiseState === "rejected") {
setTimeout(() => {
onRejected(this.PromiseResult);
})
} else if (this.PromiseState === 'pending') {
this.resolvedQueue.push(onResolved);
this.rejectedQueue.push(onRejected);
}
}
小优化:将then
中的参数变为可选。实际上原生Promise.then()
方法的参数不传参数或者只传一个参数都不会影响执行。
原理很简单,只需要在then
方法最前面判断参数是不是函数,不是则把它变成函数即可。
if (typeof onRejected !== "function") {
onRejected = (reason) => {
throw reason;
};
}
if (typeof onResolved !== "function") {
onResolved = (value) => value;
}
then()
方法返回结果,实现链式调用。原理就是返回一个新的Promise
对象。当然不能只是返回一个Promise
对象就行了,还需要判断回调得到的结果是不是Promise
对象,如果是,还得调用then()
方法,将状态变为fulfilled
或者rejected
,并变更结果为then()
方法返回的值。
如:
代码语言:javascript复制const p5 = new Promise((resolve, reject) => {
resolve('123456');
})
const p6 = p5.then(() => {
return 123567;
});
const p7 = p6.then(() => {
return new Promise((resolve, reject) => {
resolve('success');
})
})
console.log(p6);
console.log(p7);
代码语言:javascript复制then(onResolved, onRejected) {
if (typeof onRejected !== "function") {
onRejected = (reason) => {
throw reason;
};
}
if (typeof onResolved !== "function") {
onResolved = (value) => value;
}
// 以下部分代码微调
return new MyPromise((resolve, reject) => {
if (this.PromiseState === "fulfilled") {
let result;
setTimeout(() => {
result = onResolved(this.PromiseResult);
// resolvePromise判断回调得到的结果是不是MyPromise,是的话会继续执行then方法
resolvePromise(result, resolve, reject);
})
} else if (this.PromiseState === "rejected") {
setTimeout(() => {
result = onRejected(this.PromiseResult);
resolvePromise(result, resolve, reject);
})
} else if (this.PromiseState === 'pending') {
this.resolvedQueue.push((() => {
resolvePromise(onResolved(this.PromiseResult), resolve, reject)
}));
this.rejectedQueue.push(() => {
resolvePromise(onResolved(this.PromiseResult), resolve, reject)
});
}
})
}
resolvePromise
代码语言:javascript复制function resolvePromise(x, resolve, reject) {
if (x instanceof MyPromise) {
// 如果是`MyPromise`对象,则需要调用thrn方法,将状态变为 fulfilled 或者 rejecte
x.then(resolve, reject);
} else {
// 普通值直接resolve就行
resolve(x);
}
}
测试:
代码语言:javascript复制const p5 = new MyPromise((resolve, reject) => {
resolve('123456');
})
const p6 = p5.then(() => {
return 123567;
});
const p7 = p6.then(() => {
return new MyPromise((resolve, reject) => {
resolve('success');
})
})
console.log(p6);
console.log(p7);
catch方法的实现
在搞完上面的then
方法后,catch
方法就迎刃而解了。因为catch
实际上就是一个语法糖,我们也可以用then
方法来实现,只需要不传第一个参数,只传第二个参数即可。
catch(onRejected) {
return this.then(undefined, onRejected);
}
测试:
代码语言:javascript复制new MyPromise((resolve, reject) => {
reject('error');
})
.catch((reason) => {
console.error(reason);
})
完整代码
代码语言:javascript复制class MyPromise {
constructor(executor) { // 执行器函数executor
// Promise对象的状态
this.PromiseState = 'pending';
// Promise对象的结果
this.PromiseResult = undefined;
this.resolvedQueue = [];
this.rejectedQueue = [];
// 需要有`resolve`和`reject`函数,因为实例化Promise对象时需要传参`(resolve, reject) => { }`形式的函数
const resolve = (data) => {
if (this.PromiseState === 'pending') {
// 只能从`pending`状态变为`resolved`
this.PromiseState = 'fulfilled';
this.PromiseResult = data;
this.resolvedQueue.forEach(callback => callback(data));
}
};
const reject = (data) => {
if (this.PromiseState === 'pending') {
// 只能从`pending`状态变为`rejected`
this.PromiseState = 'rejected';
this.PromiseResult = data;
this.rejectedQueue.forEach(callback => callback(data));
}
};
try {
executor(resolve, reject);
} catch (err) {
reject(err);
}
}
then(onResolved, onRejected) {
if (typeof onRejected !== "function") {
onRejected = (reason) => {
throw reason;
};
}
if (typeof onResolved !== "function") {
onResolved = (value) => value;
}
return new MyPromise((resolve, reject) => {
if (this.PromiseState === "fulfilled") {
let result;
setTimeout(() => {
result = onResolved(this.PromiseResult);
resolvePromise(result, resolve, reject);
})
} else if (this.PromiseState === "rejected") {
setTimeout(() => {
result = onRejected(this.PromiseResult);
resolvePromise(result, resolve, reject);
})
} else if (this.PromiseState === 'pending') {
this.resolvedQueue.push((() => {
resolvePromise(onResolved(this.PromiseResult), resolve, reject)
}));
this.rejectedQueue.push(() => {
resolvePromise(onResolved(this.PromiseResult), resolve, reject)
});
}
})
}
catch(onRejected) {
return this.then(undefined, onRejected);
}
}
function resolvePromise(x, resolve, reject) {
if (x instanceof MyPromise) {
// 如果是`MyPromise`对象,则需要调用then方法,将状态变为 fulfilled 或者 rejecte
x.then(resolve, reject);
} else {
// 普通值直接resolve就行
resolve(x);
}
}
参考
- Promise学习笔记(二) | 赤蓝紫
- 从一道让我失眠的 Promise 面试题开始,深入分析 Promise 实现细节 - 掘金