js-手撕8
source link: https://www.clzczh.top/2022/12/24/js-%E6%89%8B%E6%92%958/
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.
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种情况已经能够实现了
// 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方法的实现
首先,简单的实现一下
then(onResolved, onRejected) {
if (this.PromiseState === "fulfilled") {
onResolved(this.PromiseResult);
} else if (this.PromiseState === "rejected") {
onRejected(this.PromiseResult);
}
}
// 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()
方法返回的值。
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);
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
function resolvePromise(x, resolve, reject) {
if (x instanceof MyPromise) {
// 如果是`MyPromise`对象,则需要调用thrn方法,将状态变为 fulfilled 或者 rejecte
x.then(resolve, reject);
} else {
// 普通值直接resolve就行
resolve(x);
}
}
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);
}
new MyPromise((resolve, reject) => {
reject('error');
})
.catch((reason) => {
console.error(reason);
})
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);
}
}
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK