在我们开始手写Promise之前我们要清楚以下七点:
- Promise 就是一个类 在执行这个类的时候 需要传递一个执行器进去 执行器会立即执行
Promise 中有三种状态 分别为 成功 fulfilled 失败 rejected 等待 pending
pending -> fulfilled pending -> rejected 一旦状态确定就不可更改
resolve和reject函数是用来更改状态的
resolve: fulfilled reject: rejected
- then方法内部做的事情就判断状态 如果状态是成功 调用成功的回调函数 如果状态是失败 调用失败回调函数 then方法是被定义在原型对象中的
- then成功回调有一个参数 表示成功之后的值 then失败回调有一个参数 表示失败后的原因
- 同一个promise对象下面的then方法是可以被调用多次的
- then方法是可以被链式调用的, 后面then方法的回调函数拿到值的是上一个then方法的回调函数的返回值
那么接下来:我们开始实现功能
Promise类核心逻辑实现
我们创建一个myPromise.js,实现最基本的Promise实现
const PENDING = "pending"; // 等待
const FULFILLED = "fulfilled"; // 成功
const REJECTED = "rejected"; // 失败
class MyPromise {
constructor(executor) {
executor(this.resolve, this.reject);
}
status = PENDING;
// 成功之后的值
value = undefined;
// 失败之后的原因
reason = undefined;
// 二者定义为箭头函数的目的是为了让箭头函数的this指向Promise对象
resolve = (value) => {
// 判断是否是等待,不是就阻止执行
if (this.status !== PENDING) return;
// 将状态更改为成功
this.status = FULFILLED;
// 传递了一个value参数,成功之后的值,保存起来
this.value = value;
};
reject = (reason) => {
// 判断是否是等待,不是就阻止执行
if (this.status !== PENDING) return;
// 将状态更改为失败
this.status = REJECTED;
// 保存失败的原因
this.reason = reason;
};
then(successCallBack, failCallBack) {
// 判断状态
if (this.status === FULFILLED) {
successCallBack(this.value);
} else if (this.status === REJECTED) {
failCallBack(this.reason);
}
}
}
module.exports = MyPromise;
加入异步逻辑
逻辑:在上面的代码的基础上,加入一个判断,如果status是pending,那么就将成功和失败的callback存储起来,等到调用resolve或者reject的时候判断是否有存储的值,有的话就就调用。
修改之后的代码:
const PENDING = "pending"; // 等待
const FULFILLED = "fulfilled"; // 成功
const REJECTED = "rejected"; // 失败
class MyPromise {
constructor(executor) {
executor(this.resolve, this.reject);
}
status = PENDING;
// 成功之后的值
value = undefined;
// 失败之后的原因
reason = undefined;
// 成功回调
successCallBack = undefined;
// 失败回调
failCallBack = undefined;
// 二者定义为箭头函数的目的是为了让箭头函数的this指向Promise对象
resolve = (value) => {
// 判断是否是等待,不是就阻止执行
if (this.status !== PENDING) return;
// 将状态更改为成功
this.status = FULFILLED;
// 传递了一个value参数,成功之后的值,保存起来
this.value = value;
// 判断成功回调是否存在,如果 存在 就调用
this.successCallBack && this.successCallBack(this.value);
};
reject = (reason) => {
// 判断是否是等待,不是就阻止执行
if (this.status !== PENDING) return;
// 将状态更改为失败
this.status = REJECTED;
// 保存失败的原因
this.reason = reason;
// 判断失败回调是否存在,如果 存在 就调用
this.failCallBack && this.failCallBack(this.reason);
};
then(successCallBack, failCallBack) {
// 判断状态
if (this.status === FULFILLED) {
successCallBack(this.value);
} else if (this.status === REJECTED) {
failCallBack(this.reason);
} else {
// 等待状态
// 将成功回调和失败回调存储起来
this.successCallBack = successCallBack;
this.failCallBack = failCallBack;
}
}
}
module.exports = MyPromise;
实现then方法多次调用添加多个处理函数
将上述的代码再次进行修正,将成功和失败两种callBack设置为数组,这样子就可以多次调用处理函数了
const PENDING = "pending"; // 等待
const FULFILLED = "fulfilled"; // 成功
const REJECTED = "rejected"; // 失败
class MyPromise {
constructor(executor) {
executor(this.resolve, this.reject);
}
status = PENDING;
// 成功之后的值
value = undefined;
// 失败之后的原因
reason = undefined;
// 成功回调
successCallBack = [];
// 失败回调
failCallBack = [];
// 二者定义为箭头函数的目的是为了让箭头函数的this指向Promise对象
resolve = (value) => {
// 判断是否是等待,不是就阻止执行
if (this.status !== PENDING) return;
// 将状态更改为成功
this.status = FULFILLED;
// 传递了一个value参数,成功之后的值,保存起来
this.value = value;
// 判断成功回调是否存在,如果 存在 就调用
// this.successCallBack && this.successCallBack(this.value);
while (this.successCallBack.length)
this.successCallBack.shift()(this.value);
};
reject = (reason) => {
// 判断是否是等待,不是就阻止执行
if (this.status !== PENDING) return;
// 将状态更改为失败
this.status = REJECTED;
// 保存失败的原因
this.reason = reason;
// 判断失败回调是否存在,如果 存在 就调用
// this.failCallBack && this.failCallBack(this.reason);
while (this.failCallBack.length) this.failCallBack.shift()(this.reason);
};
then(successCallBack, failCallBack) {
// 判断状态
if (this.status === FULFILLED) {
successCallBack(this.value);
} else if (this.status === REJECTED) {
failCallBack(this.reason);
} else {
// 等待状态
// 将成功回调和失败回调存储起来
this.successCallBack.push(successCallBack);
this.failCallBack.push(failCallBack);
}
}
}
module.exports = MyPromise;
实现then的链式调用
then方法可以链式调用,那么then要实现链式调用的话,就要在then方法里返回promise对象。要把上一个then方法返回的值传递给下一个then的话,要先找到他对应的promise对象:(then部分的代码更改如下
then(successCallBack, failCallBack) {
let promise2 = new MyPromise((resolve, reject) => {
// 判断状态
if (this.status === FULFILLED) {
let x = successCallBack(this.value);
resolve(x);
} else if (this.status === REJECTED) {
failCallBack(this.reason);
} else {
// 等待状态
// 将成功回调和失败回调存储起来
this.successCallBack.push(successCallBack);
this.failCallBack.push(failCallBack);
}
});
return promise2;
}
其中,我们还有一些优化的点:
1.判断这个代码里的x是普通值还是promise对象
2.是普通值就直接调用resolve
3.是promise就根据promise对象返回的结果,决定是调用resolve还是reject
if (this.status === FULFILLED) {
let x = successCallBack(this.value);
// 1.判断这个代码里的x是普通值还是promise对象
// 2.是普通值就直接调用resolve
// 3.是promise就根据promise对象返回的结果,决定是调用resolve还是reject
resolvePromise(x, resolve, reject);
resolve(x);
}
function resolvePromise(x, resolve, reject) {
if (x instanceof MyPromise) {
// x 是promise
// x.then(
// (value) => resolve(value),
// (reason) => reject(reason)
// );
x.then(resolve, reject);
} else {
// x 是普通值
resolve(x);
}
}
then链式调用识别Promise对象自返回
then方法之后是返回一个Promise对象的,但是如果你返回了当前then方法返回的promise的话,就会发生循环调用
比如:
var promise = new Promise((resolve, reject) => {
resolve(100);
});
var p1 = promise.then((value) => {
// 这里毫无疑问是100
console.log(value);
// 我返回了本身的promise
return p1;
});
那么该怎么处理?只要在之前我们处理的方法:resolvePromise加上一个promiseReturn参数将他返回的值传递进去做一下校验就行了,另外需要注意的是,要把FULFILLED的代码块进行一下异步转化(包一层setTimeout,时间设置为0ms)
function resolvePromise(x, resolve, reject, promiseReturn) {
if (x === promiseReturn) {
reject(new TypeError("Chaining cycle detected for promise #<Promise>"));
}
if (x instanceof MyPromise) {
// x 是promise
// x.then(
// (value) => resolve(value),
// (reason) => reject(reason)
// );
x.then(resolve, reject);
} else {
// x 是普通值
resolve(x);
}
}
let promise2 = new MyPromise((resolve, reject) => {
// 判断状态
if (this.status === FULFILLED) {
// 这样就会变成异步代码,会在所有代码执行完之后执行,就可以获取到promise2了
setTimeout(() => {
let x = successCallBack(this.value);
// 1.判断这个代码里的x是普通值还是promise对象
// 2.是普通值就直接调用resolve
// 3.是promise就根据promise对象返回的结果,决定是调用resolve还是reject
resolvePromise(x, resolve, reject, promise2);
}, 0);
} else if (this.status === REJECTED) {
failCallBack(this.reason);
} else {
// 等待状态
// 将成功回调和失败回调存储起来
this.successCallBack.push(successCallBack);
this.failCallBack.push(failCallBack);
}
});
捕获错误及then链式调用其他状态代码的补充
到目前为止我们没有进行任何的错误处理,还是需要捕获一下错误
1.执行器捕获,在构造器中修改一下try catch
constructor(executor) {
try {
// 如果执行器发生错误会走到catch
executor(this.resolve, this.reject);
} catch (e) {
this.reject(e);
}
}
2.then方法里面的回调函数(then回调执行报错也要捕获)
if (this.status === FULFILLED) {
// 这样就会变成异步代码,会在所有代码执行完之后执行,就可以获取到promise2了
setTimeout(() => {
try {
let x = successCallBack(this.value);
// 1.判断这个代码里的x是普通值还是promise对象
// 2.是普通值就直接调用resolve
// 3.是promise就根据promise对象返回的结果,决定是调用resolve还是reject
resolvePromise(x, resolve, reject, promise2);
} catch (e) {
reject(e);
}
}, 0);
}
3.失败的时候也需要像then那样链式调用并且捕获错误:
else if (this.status === REJECTED) {
setTimeout(() => {
try {
let x = failCallBack(this.reason);
// 1.判断这个代码里的x是普通值还是promise对象
// 2.是普通值就直接调用resolve
// 3.是promise就根据promise对象返回的结果,决定是调用resolve还是reject
resolvePromise(x, resolve, reject, promise2);
} catch (e) {
reject(e);
}
}, 0);
4.异步情况(push那里
else {
// 等待状态
// 将成功回调和失败回调存储起来
this.successCallBack.push(() => {
setTimeout(() => {
try {
let x = successCallBack(this.value);
// 1.判断这个代码里的x是普通值还是promise对象
// 2.是普通值就直接调用resolve
// 3.是promise就根据promise对象返回的结果,决定是调用resolve还是reject
resolvePromise(x, resolve, reject, promise2);
} catch (e) {
reject(e);
}
}, 0);
});
this.failCallBack.push(() => {
setTimeout(() => {
try {
let x = failCallBack(this.reason);
// 1.判断这个代码里的x是普通值还是promise对象
// 2.是普通值就直接调用resolve
// 3.是promise就根据promise对象返回的结果,决定是调用resolve还是reject
resolvePromise(x, resolve, reject, promise2);
} catch (e) {
reject(e);
}
}, 0);
});
}
这样修改之后在resolve和reject调用此函数也就不需要传参数了
将then方法的参数变为可选参数
先看一段代码吧:
var promise = new Promise((resolve, reject) => {
reject(100)
})
promise.then().then().then((value) => {
console.log(value)
})
链式调用了三次then,前两次没有任何参数,于是promise就依次向下传递
在我们自己的promise中如何做出这个?
添加这个就好,有就传递有的callback,没有就补一个
Promise.all方法实现
因为Promise.all并不依赖于特定的Promise实例,所以它被设计为静态方法,使得开发者可以方便地对多个Promise对象进行并行处理,无需先创建一个Promise实例
// 静态方法
static all(array) {
// 返回的结果数组
let resultArr = [];
// 用来统计addData执行的次数
let index = 0;
return new MyPromise((resolve, reject) => {
// 添加数据到resultArr
function addData(key, value) {
resultArr[key] = value;
index++;
// 如果index达到了array的长度,就resolve掉
if (index === array.length) {
resolve(resultArr);
}
}
// 在循环内进行判定,是MyPromise的就走then再addData,不是就直接addData
for (let i = 0; i < array.length; i++) {
let current = array[i];
if (current instanceof MyPromise) {
current.then(
(value) => {
addData(i, value);
},
(reason) => reject(reason)
);
} else {
addData(i, array[i]);
}
}
});
}
Promise.resolve方法的实现
Promise.resolve() 是一个 Promise API 的方法,它会返回一个解析(resolved)状态的 Promise 对象,可以看作是创建 Promise 对象的一种快捷方式。
function p1() {
return new Promise(function (resolve, reject) {
resolve(100);
});
}
// 10
Promise.resolve(10).then((value) => console.log(value));
// 100
Promise.resolve(p1()).then((value) => console.log(value));
在resolve内部判断,是给的普通值还是Promise对象,如果是Promise对象,会原封不动的把Promise对象作为resolve的返回值。
static resolve(value) {
if (value instanceof MyPromise) {
return value;
} else {
return new MyPromise((resolve) => resolve(value));
}
}
Promise.finally
1.无论当前Promise对象最终状态成功失败与否,都会执行
2.finally的后面可以链式调用then方法来获取当前Promise对象最终返回的结果
此方法不是静态方法,需要定义到原型上
finally (callback) {
return this.then(
(value) => {
// 转为Promise等待执行callback
return MyPromise.resolve(callback()).then(() => value);
},
(reason) => {
return MyPromise.resolve(callback()).then(() => {
throw reason;
});
}
);
};
Promise catch
处理当前Promise对象失败的方法
比较简单:
catch(failCallBack) {
return this.then(undefined, failCallBack);
}
在文章的末尾我贴上一个完整的Promise自我实现的代码吧:
const PENDING = "pending"; // 等待
const FULFILLED = "fulfilled"; // 成功
const REJECTED = "rejected"; // 失败
class MyPromise {
constructor(executor) {
try {
// 如果执行器发生错误会走到catch
executor(this.resolve, this.reject);
} catch (e) {
this.reject(e);
}
}
status = PENDING;
// 成功之后的值
value = undefined;
// 失败之后的原因
reason = undefined;
// 成功回调
successCallBack = [];
// 失败回调
failCallBack = [];
// 二者定义为箭头函数的目的是为了让箭头函数的this指向Promise对象
resolve = (value) => {
// 判断是否是等待,不是就阻止执行
if (this.status !== PENDING) return;
// 将状态更改为成功
this.status = FULFILLED;
// 传递了一个value参数,成功之后的值,保存起来
this.value = value;
// 判断成功回调是否存在,如果 存在 就调用
// this.successCallBack && this.successCallBack(this.value);
while (this.successCallBack.length) this.successCallBack.shift()();
};
reject = (reason) => {
// 判断是否是等待,不是就阻止执行
if (this.status !== PENDING) return;
// 将状态更改为失败
this.status = REJECTED;
// 保存失败的原因
this.reason = reason;
// 判断失败回调是否存在,如果 存在 就调用
// this.failCallBack && this.failCallBack(this.reason);
while (this.failCallBack.length) this.failCallBack.shift()();
};
finally(callback) {
return this.then(
(value) => {
// 转为Promise等待执行callback
return MyPromise.resolve(callback()).then(() => value);
},
(reason) => {
return MyPromise.resolve(callback()).then(() => {
throw reason;
});
}
);
}
catch(failCallBack) {
return this.then(undefined, failCallBack);
}
then(successCallBack, failCallBack) {
successCallBack = successCallBack ? successCallBack : (value) => value;
failCallBack = failCallBack
? failCallBack
: (reason) => {
throw reason;
};
let promise2 = new MyPromise((resolve, reject) => {
// 判断状态
if (this.status === FULFILLED) {
// 这样就会变成异步代码,会在所有代码执行完之后执行,就可以获取到promise2了
setTimeout(() => {
try {
let x = successCallBack(this.value);
// 1.判断这个代码里的x是普通值还是promise对象
// 2.是普通值就直接调用resolve
// 3.是promise就根据promise对象返回的结果,决定是调用resolve还是reject
resolvePromise(x, resolve, reject, promise2);
} catch (e) {
reject(e);
}
}, 0);
} else if (this.status === REJECTED) {
setTimeout(() => {
try {
let x = failCallBack(this.reason);
// 1.判断这个代码里的x是普通值还是promise对象
// 2.是普通值就直接调用resolve
// 3.是promise就根据promise对象返回的结果,决定是调用resolve还是reject
resolvePromise(x, resolve, reject, promise2);
} catch (e) {
reject(e);
}
}, 0);
} else {
// 等待状态
// 将成功回调和失败回调存储起来
this.successCallBack.push(() => {
setTimeout(() => {
try {
let x = successCallBack(this.value);
// 1.判断这个代码里的x是普通值还是promise对象
// 2.是普通值就直接调用resolve
// 3.是promise就根据promise对象返回的结果,决定是调用resolve还是reject
resolvePromise(x, resolve, reject, promise2);
} catch (e) {
reject(e);
}
}, 0);
});
this.failCallBack.push(() => {
setTimeout(() => {
try {
let x = failCallBack(this.reason);
// 1.判断这个代码里的x是普通值还是promise对象
// 2.是普通值就直接调用resolve
// 3.是promise就根据promise对象返回的结果,决定是调用resolve还是reject
resolvePromise(x, resolve, reject, promise2);
} catch (e) {
reject(e);
}
}, 0);
});
}
});
return promise2;
}
// 静态方法
static all(array) {
// 返回的结果数组
let resultArr = [];
// 用来统计addData执行的次数
let index = 0;
return new MyPromise((resolve, reject) => {
// 添加数据到resultArr
function addData(key, value) {
resultArr[key] = value;
index++;
// 如果index达到了array的长度,就resolve掉
if (index === array.length) {
resolve(resultArr);
}
}
// 在循环内进行判定,是MyPromise的就走then再addData,不是就直接addData
for (let i = 0; i < array.length; i++) {
let current = array[i];
if (current instanceof MyPromise) {
current.then(
(value) => {
addData(i, value);
},
(reason) => reject(reason)
);
} else {
addData(i, array[i]);
}
}
});
}
static resolve(value) {
if (value instanceof MyPromise) {
return value;
} else {
return new MyPromise((resolve) => resolve(value));
}
}
}
function resolvePromise(x, resolve, reject, promiseReturn) {
if (x === promiseReturn) {
reject(new TypeError("Chaining cycle detected for promise #<Promise>"));
}
if (x instanceof MyPromise) {
// x 是promise
// x.then(
// (value) => resolve(value),
// (reason) => reject(reason)
// );
x.then(resolve, reject);
} else {
// x 是普通值
resolve(x);
}
}
module.exports = MyPromise;