简言
最近在做前端知识的复习和整理,有了一些自己新的体会。更多在于记录,通过反复的温习,写笔记消除自己以前学习知识点的误区
什么是Promise
Promise是一种异步处理解决方案,为了解决callback造成的异步回调地狱问题。
Promise寓意承诺,当我们把逻辑处理交给Promise的时候,Promise承诺一定会给结果(成功 or 失败)
Promise有三大状态
pending、fulfilled、rejected
基本的Promise实现(同步)
- 当我们将逻辑处理交给Promise的时候,Promise要求我们等待结果(此时Promise的状态为
pending) - 当Promise处理完逻辑后会承诺结果,此时Promise如果处理成功,则状态
pending => fulfilled,如果处理失败或者逻辑本身存在语法错误则状态pending => rejected - Promise的处理具有
状态单向性,即Promise的状态不可以回转。 - Promise可以通过then方法将处理的结果交给我们,有两个函数接收(成功回调/失败回调)
class MyPromise {
constructor(executor) {
this.state = "pending"; // Promise初始状态为pending
this.value = undefined; // Promise成功时存储的值
this.reason = undefined; // Promise失败时存储的原因
const resolve = (value) => {
this.state = "fulfilled"; // 调用resolve的时候将状态改为"fulfilled"
this.value = value; // 并存储成功的值
};
const reject = (reason) => {
this.state = "rejected"; // 调用reject的时候将状态改为"rejected"
this.reason = reason; // 并存储失败的原因
};
executor(resolve, reject); // new实例化Promise时,传入的excutor函数会立即执行,并且传入resolve和reject方法,用于修改Promise状态以及
}
then(onFullfilled, onRejected) { // 调用then方法的时候接收一个拿成功值的方法和一个拿失败原因的方法
if (this.state === "fulfilled") { // 调用时判断当前Promise的状态
onFullfilled(this.value);
}
if (this.state === "rejected") {
onRejected(this.reason);
}
}
}
基本的Promise实现(异步)
若new Promise中是一个异步执行的逻辑时,例如:
const p1 = new MyPromise((resolve)=>{
setTimeout(()=>{
resolve(1)
}, 1000)
})
p1.then((res)=>{
console.log(res)
}, (err)=>{
console.log(err)
})
// p1多次调用then方法
p1.then((res)=>{
console.log(res)
}, (err)=>{
console.log(err)
})
当new MyPromise执行的时候,由于resolve在1秒后执行,因此此时p1调用then方法时,p1状态还是pending,因此我们需要做如下改动:
class MyPromise {
constructor(executor) {
this.state = "pending";
this.value = undefined;
this.reason = undefined;
this.onFullfilledCallbacks = []; // 保存异步逻辑执行完成之后要执行的成功的回调函数
this.onRejectedCallbacks = []; // 保存异步逻辑执行完成之后要执行的失败的回调函数
const resolve = (value) => {
if(this.state === 'pending') {
this.state = "fulfilled";
this.value = value;
this.onFullfilledCallbacks.forEach((fn) => fn()); // 当异步处理完成执行resolve时,遍历执行成功回调集合中的函数
}
};
const reject = (reason) => {
if(this.state === 'pending') {
this.state = "rejected";
this.reason = reason;
this.onRejectedCallbacks.forEach((fn) => fn()); // 当异步处理完成执行reject时,遍历执行成功回调集合中的函数
}
};
executor(resolve, reject);
}
then(onFullfilled, onRejected) {
if (this.state === "fulfilled") {
onFullfilled(this.value);
}
if (this.state === "rejected") {
onRejected(this.reason);
}
// 当调用then时发现Promise实例化对象自身的状态为pending,则将传入的成功/失败回调函数,保存在实例化对象自身的回调函数集合onFullfilledCallbacks,onRejectedCallbacks中
if (this.state === "pending") {
// 当回调集合遍历执行push的函数时会执行拿到最新value/reason的,onFullfilled和onRejected
this.onFullfilledCallbacks.push(() => {
onFullfilled(this.value);
});
this.onRejectedCallbacks.push(() => {
onRejected(this.reason);
});
}
}
}
Promise的链式调用
我们在使用Promise时,会发现Promise可以进行链式调用
new Promise().then().then().then()...
并且我们也知道只有Promise的实例化对象才能调用then方法,因此我们每次调用.then都需要返回一个Promise实例化对象
调用then方法不能返回this,因为this是上一次的Promise,由于Promise的状态具有单向性,因此调用then方法时需要返回一个全新的Promise对象
根据下面代码,改动then
const p1 = new MyPromise((resolve, reject) => {
resolve(1);
});
const p2 = p1.then(
(res) => {
return res + 1;
},
(err) => {
console.log(err);
}
);
p2.then(
(res) => {
console.log(res);
},
(err) => {
console.log(err);
}
);
首先改动一下then方法
then(onFullfilled, onRejected) {
const p2 = new MyPromise((resolve, reject) => {
// Promise的excutor函数是同步执行的,因此根据状态调用回调的过程可以放进MyPromise中
if (this.state === "fulfilled") {
onFullfilled(this.value);
}
if (this.state === "rejected") {
onRejected(this.reason);
}
if (this.state === "pending") {
this.onFullfilledCallbacks.push(() => {
onFullfilled(this.value);
});
this.onRejectedCallbacks.push(() => {
onRejected(this.reason);
});
}
});
// 因为要实现链式调用,因此需要返回一个全新的Promise对象
return p2;
}
那么我们怎么拿到p2.then中的res呢,这里我们需要引入一个中间变量x
then(onFullfilled, onRejected) {
const p2 = new MyPromise((resolve, reject) => {
let x;
if (this.state === "fulfilled") {
// 1. this.value是p1 的this.value
// 2. onFullfilled是p1.then的成功回调函数
// 3. 将p1.then返回结果存入中间变量 x
x = onFullfilled(this.value);
// 要p2.then的res拿到x这个值,就必须调用p2的resolve,这样我们才能
// 1. 将p2的状态改为fulfilled
// 2. 并且将x存入p2的this.value
// 3. 此时p2.then的res就能拿到这个x值了
resolve(x)
}
// ...
});
return p2;
}
}
同理,那么处理失败情况和异步情况的话
then(onFullfilled, onRejected) {
const p2 = new MyPromise((resolve, reject) => {
let x;
if (this.state === "fulfilled") {
x = onFullfilled(this.value);
resolve(x);
}
// 失败情况和异步情况就照成功情况来
if (this.state === "rejected") {
x = onRejected(this.reason);
resolve(x);
}
if (this.state === "pending") {
this.onFullfilledCallbacks.push(() => {
x = onFullfilled(this.value);
resolve(x);
});
this.onRejectedCallbacks.push(() => {
x = onRejected(this.reason);
resolve(x);
});
}
});
return p2;
}
}
x可能是成功值或失败值
如果我们参考Promise
const p1 = new Promise((resolve, reject)=>{
resolve(1)
})
const p2 = p1.then(()=>{
return new Promise((resolve, reject)=>{
resolve(10) // p2.then -> 10 'success'
// reject(10) // p2.then -> 10 'error'
})
}, (err)=>{
return err + 2
})
p2.then((res)=>{
console.log(res, 'success')
}, (err) => {
console.log(err, 'error')
})
也就是说x 如果得到的是一个Promise对象,那么后续进行.then操作(上述Promise例子中p2.then)那么就可能是成功结果也可能是失败的结果
resolvePromise
let x;
if (this.state === "fulfilled") {
x = onFullfilled(this.value);
// resolve(x);
// resolvePromise是根据 x 的值类型处理 x 是resolve还是reject的函数
resolvePromise(x, resolve, reject);
}
// function resolvePromise(x, resolve, reject) {
// 根据PromiseA+规范,onFullfilled的返回值不能等于.then的返回值
// }
// 因此我们需要传入p2(then方法的返回值)
if (this.state === "fulfilled") {
// 使用setTimeout是因为,此时的p2还没初始化,因此当调用new MyPromise同步执行完成之后,再执行setTimeout的回调函数就能拿到p2了
// 并且为了防止使用p2出错,这里可以添加try catch
setTimeout(()=>{
try {
x = onFullfilled(this.value);
// resolve(x);
resolvePromise(p2, x, resolve, reject);
} catch (err) {
reject(err)
}
}, 0)
}
function resolvePromise(p2, x, resolve, reject) {
let called = undefined; // 调用锁
if (p2 === x) {
return reject(new TypeError("Chaining cycle detected for promise"));
}
// 判断函数是一个对象或者是一个函数
if ((typeof x === "object" && x !== null) || typeof x === "function") {
try {
// 使用try catch是因为x.then可能会失败
let then = x.then;
if (typeof then === "function") {
// 这个x是一个有then方法的对象,也可能是个Promise(这里认为是一个Promise对象)
// promise的then方法接收两个方法 参数,一个是成功的回调,一个是失败的回调
then.call(
x,
(y) => {
if (called) return;
called = true;
// resolve(y)
// 这里的y可能也是一个Promise,因此需要递归判断,但这个递归因为called锁的问题,递归的每一层只能调用一次
resolvePromise(p2, y, resolve, reject);
},
(r) => {
if (called) return;
called = true;
reject(r);
}
);
} else {
// 普通对象
if (called) return;
called = true;
resolve(x);
}
} catch (err) {
if (called) return;
called = true;
reject(err);
}
} else {
// 针对普通值而言number, string, boolean
resolve(x);
}
}
完整实例
function resolvePromise(p2, x, resolve, reject) {
let called; // 调用锁
if (p2 === x) {
return reject(new TypeError("Chaining cycle detected for promise"));
}
// 判断函数是一个对象或者是一个函数
if ((typeof x === "object" && x !== null) || typeof x === "function") {
try {
// 使用try catch是因为x.then可能会失败
let then = x.then;
if (typeof then === "function") {
// 这个x是一个有then方法的对象,也可能是个Promise(这里认为是一个Promise对象)
// promise的then方法接收两个方法 参数,一个是成功的回调,一个是失败的回调
then.call(
x,
(y) => {
if (called) return;
called = true;
// resolve(y)
// 这里的y可能也是一个Promise,因此需要递归判断,但这个递归因为called锁的问题,递归的每一层只能调用一次
resolvePromise(p2, y, resolve, reject);
},
(r) => {
if (called) return;
called = true;
reject(r);
}
);
} else {
// 普通对象
if (called) return;
called = true;
resolve(x);
}
} catch (err) {
if (called) return;
called = true;
reject(err);
}
} else {
// 针对普通值而言number, string, boolean
resolve(x);
}
}
function isFunction(fn) {
return typeof fn === "function";
}
class MyPromise {
constructor(executor) {
this.state = "pending";
this.value = undefined;
this.reason = undefined;
this.onFullfilledCallbacks = [];
this.onRejectedCallbacks = [];
const resolve = (value) => {
this.state = "fulfilled";
this.value = value;
this.onFullfilledCallbacks.forEach((fn) => fn());
};
const reject = (reason) => {
this.state = "rejected";
this.reason = reason;
this.onRejectedCallbacks.forEach((fn) => fn());
};
// 同步执行代码时报错
try {
executor(resolve, reject);
} catch (err) {
reject(err);
}
}
then(onFullfilled, onRejected) {
onFullfilled = isFunction(onFullfilled) ? onFullfilled : (data) => data;
onRejected = isFunction(onRejected)
? onRejected
: (err) => {
throw err;
};
const p2 = new MyPromise((resolve, reject) => {
let x;
if (this.state === "fulfilled") {
setTimeout(() => {
try {
x = onFullfilled(this.value);
resolvePromise(p2, x, resolve, reject);
} catch (err) {
reject(err);
}
}, 0);
}
if (this.state === "rejected") {
setTimeout(() => {
try {
x = onRejected(this.reason);
resolvePromise(p2, x, resolve, reject);
} catch (err) {
reject(err);
}
}, 0);
}
if (this.state === "pending") {
this.onFullfilledCallbacks.push(() => {
setTimeout(() => {
try {
x = onFullfilled(this.value);
// resolve(x);
resolvePromise(p2, x, resolve, reject);
} catch (err) {
reject(err);
}
}, 0);
});
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
try {
x = onRejected(this.reason);
// resolve(x);
resolvePromise(p2, x, resolve, reject);
} catch (err) {
reject(err);
}
}, 0);
});
}
});
return p2;
}
}
// 测试Promise
MyPromise.defer = MyPromise.deferred = function () {
let dfd = {};
dfd.promise = new MyPromise((resolve, reject) => {
dfd.resolve = resolve;
dfd.reject = reject;
});
return dfd;
};
module.exports = MyPromise;
测试你的Promise
npm install promises-aplus-tests -g
promises-aplus-tests 你的Promise代码路径