在工作中我们经常会使用到Promise来对请求进行封装,我们已经知道了如何使用Promise,但是相对于使用来说我们更应该清楚内部的逻辑是如何实现的,因为相对使用来说我们更因该学习它的思想。下面让我们从最基本的使用来一步步解刨实现Promise类的核心逻辑。
Promise基本实现
我们可以基于平时工作中使用Promise的方式来进行分析:
-
Promise就是一个类 在执行这个类的时候 需要传递一个执行器进去 执行器会立即执行 -
Promise中有三种状态 分别为 等待pending成功fulfilled失败rejected- pending -> fulfilled
- pending -> rejected
- 一旦状态确定就不可改变
- resolve和reject就是用来改变状态的
- resolve: fulfilled
- reject: rejected
- then方法内部做的事情就是判断状态 如果状态是成功 调用成功的回调函数 如果状态是失败 调用失败的回调函数 then方法是被定义在原型对象中的
- then方法成功回调有一个参数 表示成功之后的值 then方法失败回调有一个参数 表示失败的原因
分析完毕,是不是思路已经在大脑中很明确了,好,那我们直接上代码:
- 接着我们创建index.js
/*
思路:
1. Promise 就是一个类 在执行这个类的时候 需要传递一个执行器进去 执行器会立即执行
2. Promise 中有三种状态 分别为 等待 pending 成功 fulfilled 失败 rejected
pending -> fulfilled
pending -> rejected
一旦状态确定就不可改变
3. resolve和reject就是用来改变状态的
resolve: fulfilled
reject: rejected
4. then方法内部做的事情就是判断状态 如果状态是成功 调用成功的回调函数 如果状态是失败 调用失败的回调函数 then方法是被定义在原型对象中的
5. then方法成功回调有一个参数 表示成功之后的值 then方法失败回调有一个参数 表示失败的原因
*/
const MyPromise = require('./myPromise');
let promise = new MyPromise((resolve, reject) => {
resolve("成功");
reject("失败");
});
promise.then(success => {
console.log(success)
},error => {
console.log(error)
})
- 我们把自己手写的
Promise放到一个单独的js文件中,myPromise.js
const PENDING = "pending"; // 等待
const FULFILLED = "fulfilled"; // 成功
const REJECTED = "rejected"; // 失败
class MyPromise {
constructor(executor) {
executor(this.resolve, this.reject);
}
// promsie 状态
status = PENDING;
// 成功之后的值
value = undefined;
// 失败后的原因
reason = undefined;
resolve = (value) => {
// 如果状态不是等待 阻止程序向下执行
if (this.status !== PENDING) return;
// 将状态更改为成功
this.status = FULFILLED;
// 保存成功之后的值
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;
Promise 实现加入异步逻辑
在上述基本实现的Promise中是没有考虑异步情况的,接下来我们在index.js的执行器中加入异步代码,看看会出现什么问题:
const MyPromise = require('./myPromise');
let promise = new MyPromise((resolve, reject) => {
//异步代码
setTimeout(()=>{
resolve("成功");
},2000)
// reject("失败");
});
promise.then(success => {
console.log(success)
},error => {
console.log(error)
})
我们会发现主线程的代码不会等待异步代码执行完成之后执行,所以promise.then()会马上执行这时候我们进入then方法的代码发现Promise的状态还是等待(pending)所以结合then中的逻辑发现并没有判断等待的状态:
//判断状态
then = (successCallback, failCallback) => {
//判断状态
if (this.status === FULFILLED) {
successCallback(this.value);
} else if (this.status === REJECTED) {
failCallback(this.reason);
}
};
接下来我们就去处理下等待状态应该做的事情,我们分析下:
- 如果是等待状态不管是成功还是失败都调不了,所以应该吧成功和失败的回调临时存起来
- 当调用
resolve(成功)或者reject(失败)的时候我们在调用
整体加入异步逻辑代码如下:
const PENDING = "pending"; // 等待
const FULFILLED = "fulfilled"; // 成功
const REJECTED = "rejected"; // 失败
class MyPromise {
constructor(executor) {
executor(this.resolve, this.reject);
}
// promsie 状态
status = PENDING;
// 成功之后的值
value = undefined;
// 失败后的原因
reason = undefined;
//成功回调
successCallback=undefined
//失败回调
failCallback=undefined
resolve = (value) => {
// 如果状态不是等待 阻止程序向下执行
if (this.status !== PENDING) return;
// 将状态更改为成功
this.status = FULFILLED;
// 保存成功之后的值
this.value = value;
//判断成功回调是否存在
if(this.failCallback)this.failCallback(this.value)
};
reject = (reason) => {
// 如果状态不是等待 阻止程序向下执行
if (this.status !== PENDING) return;
// 将状态更改为失败
this.status = REJECTED;
// 保存失败后的原因
this.reason = reason;
//判断失败回调是否存在
if(this.failCallback)this.failCallback(this.value)
};
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
}
};
}
Promise 实现then方法多次调用
同一个Promise对象下的then 方法是可以被多次调用的,当then方法被调用的时候then方法中的回调函数都是要被调用的,我们看下如何处理:
- 和上述一样分为同步和异步两种情况
- 同步情况不需要特殊处理
- 异步情况每个
then方法的回调函数都应该存储起来,所以我们应该用数组
代码如下:
myPromise.js
const PENDING = "pending"; // 等待
const FULFILLED = "fulfilled"; // 成功
const REJECTED = "rejected"; // 失败
class MyPromise {
constructor(executor) {
executor(this.resolve, this.reject);
}
// promsie 状态
status = PENDING;
// 成功之后的值
value = undefined;
// 失败后的原因
reason = undefined;
//成功回调
successCallback=[]
//失败回调
failCallback=[]
resolve = (value) => {
// 如果状态不是等待 阻止程序向下执行
if (this.status !== PENDING) return;
// 将状态更改为成功
this.status = FULFILLED;
// 保存成功之后的值
this.value = value;
//判断成功回调是否存在 每次调用第一个 并且删除
while(this.successCallback.length) this.successCallback.shift()(this.value)
};
reject = (reason) => {
// 如果状态不是等待 阻止程序向下执行
if (this.status !== PENDING) return;
// 将状态更改为失败
this.status = REJECTED;
// 保存失败后的原因
this.reason = reason;
//判断失败回调是否存在 循环数组的元素 每次调用第一个 并且删除
while(this.failCallback.length) this.failCallback.shift()(this.value)
};
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;
index.js
/*
思路:
1. Promise 就是一个类 在执行这个类的时候 需要传递一个执行器进去 执行器会立即执行
2. Promise 中有三种状态 分别为 等待 pending 成功 fulfilled 失败 rejected
pending -> fulfilled
pending -> rejected
一旦状态确定就不可改变
3. resolve和reject就是用来改变状态的
resolve: fulfilled
reject: rejected
4. then方法内部做的事情就是判断状态 如果状态是成功 调用成功的回调函数 如果状态是失败 调用失败的回调函数 then方法是被定义在原型对象中的
5. then方法成功回调有一个参数 表示成功之后的值 then方法失败回调有一个参数 表示失败的原因
6. 同一个promise对象下面的then方法是可以被调用多次的
*/
const MyPromise = require('./myPromise');
let promise = new MyPromise((resolve, reject) => {
setTimeout(()=>{
resolve("成功");
},2000)
// reject("失败");
});
promise.then(success => {
console.log(success)
},error => {
console.log(error)
})
promise.then(success => {
console.log(success)
},error => {
console.log(error)
})
promise.then(success => {
console.log(success)
},error => {
console.log(error)
})
Promise 实现then方法的链式调用(一)
Promise对象下边的then方法是可以被链式调用的,后面的then方法回调函数拿到的值是上一个then方法回调的返回值比如:
//then方法是可以被链式调用的, 后面then方法的回调函数拿到值的是上一个then方法的回调函数的返回值
let promise = new MyPromise((resolve, reject) => {
// setTimeout(() => {
resolve("成功");
// }, 2000);
// reject("失败");
});
promise
.then((success) => {
console.log(success);
return 100; //第一个返回100
})
.then((value) => {
console.log(value); //100
});
- 既然可以链式调用,那么说明then 方法需要返回一个Promise
- 在返回的Promise 将状态传递一下 ,并且把100最为成功的值或者失败的理由
- 不支持异步
代码如下:
const PENDING = "pending"; // 等待
const FULFILLED = "fulfilled"; // 成功
const REJECTED = "rejected"; // 失败
class MyPromise {
constructor(executor) {
executor(this.resolve, this.reject);
}
// promsie 状态
status = PENDING;
// 成功之后的值
value = undefined;
// 失败后的原因
reason = undefined;
//成功回调
successCallback = [];
//失败回调
failCallback = [];
resolve = (value) => {
// 如果状态不是等待 阻止程序向下执行
if (this.status !== PENDING) return;
// 将状态更改为成功
this.status = FULFILLED;
// 保存成功之后的值
this.value = value;
//判断成功回调是否存在
// if(this.failCallback)this.failCallback(this.value)
while (this.successCallback.length) this.successCallback.shift()(this.value);
};
reject = (reason) => {
// 如果状态不是等待 阻止程序向下执行
if (this.status !== PENDING) return;
// 将状态更改为失败
this.status = REJECTED;
// 保存失败后的原因
this.reason = reason;
//判断失败回调是否存在
while (this.failCallback.length) this.failCallback.shift()(this.reason);
};
then = (successCallback, failCallback) => {
let promsie2 = new MyPromise((resolve, reject) => {
//判断状态
if (this.status === FULFILLED) {
let x = successCallback(this.value);
resolve(x);
} else if (this.status === REJECTED) {
let x = failCallback(this.reason);
reject(x);
} else {
this.successCallback.push(successCallback);
this.failCallback.push(failCallback);
}
});
return promsie2;
};
}
module.exports = MyPromise;
Promise 实现then方法的链式调用(二)
上述我们实现了then方法链式调用,但是我们的返回值可以返回一个普通值也可以返回一个Promise,这时候我们就需要再次就行逻辑处理
- 判断 x 的值是普通值还是promise对象
- 如果是普通值 直接调用resolve
- 如果是promise对象 查看promsie对象返回的结果
- 再根据promise对象返回的结果 决定调用resolve 还是调用reject
const PENDING = "pending"; // 等待
const FULFILLED = "fulfilled"; // 成功
const REJECTED = "rejected"; // 失败
class MyPromise {
constructor(executor) {
executor(this.resolve, this.reject);
}
// promsie 状态
status = PENDING;
// 成功之后的值
value = undefined;
// 失败后的原因
reason = undefined;
//成功回调
successCallback = [];
//失败回调
failCallback = [];
resolve = (value) => {
// 如果状态不是等待 阻止程序向下执行
if (this.status !== PENDING) return;
// 将状态更改为成功
this.status = FULFILLED;
// 保存成功之后的值
this.value = value;
//判断成功回调是否存在
// if(this.failCallback)this.failCallback(this.value)
while (this.successCallback.length) this.successCallback.shift()(this.value);
};
reject = (reason) => {
// 如果状态不是等待 阻止程序向下执行
if (this.status !== PENDING) return;
// 将状态更改为失败
this.status = REJECTED;
// 保存失败后的原因
this.reason = reason;
//判断失败回调是否存在
while (this.failCallback.length) this.failCallback.shift()(this.reason);
};
then = (successCallback, failCallback) => {
let promsie2 = new MyPromise((resolve, reject) => {
//判断状态
if (this.status === FULFILLED) {
let x = successCallback(this.value);
// 判断 x 的值是普通值还是promise对象
// 如果是普通值 直接调用resolve
// 如果是promise对象 查看promsie对象返回的结果
// 再根据promise对象返回的结果 决定调用resolve 还是调用reject
resolvePromoise(x,resolve,reject)
} else if (this.status === REJECTED) {
let x = failCallback(this.reason);
reject(x);
} else {
this.successCallback.push(successCallback);
this.failCallback.push(failCallback);
}
});
return promsie2;
};
}
function resolvePromoise(x,resolve,reject){
if( x instanceof MyPromise){
//promise
x.then(resolve,reject)
}else{
//普通值
resolve(x)
}
}
module.exports = MyPromise;
Promise then方法链式调用识别Promise对象自返回
then方法的回调中虽然可以返回Promise对象,但是有一种是例外的,在then方法的回调函数中不能返回当前then方法所返回的Promise对象的,因为会发生循环调用,这样是不会被允许的。
演示:
var promise = new Promise((resolve, reject) => {
resolve(100);
});
var p1 = promise.then((value) => {
return p1
})
p1.then(()=>{
},(err)=>{
console.log(err.message) // index.html:1 Uncaught (in promise) TypeError: Chaining cycle detected for promise #<Promise>
})
我们发现原本的Promise已经去做出了处理,我们需要在自己Promise中也去处理下
- 我们只需要判断当前返回的Promise 和 回调中返回的是不是同一个Promise就可以了
const PENDING = "pending"; // 等待
const FULFILLED = "fulfilled"; // 成功
const REJECTED = "rejected"; // 失败
class MyPromise {
constructor(executor) {
executor(this.resolve, this.reject);
}
// promsie 状态
status = PENDING;
// 成功之后的值
value = undefined;
// 失败后的原因
reason = undefined;
//成功回调
successCallback = [];
//失败回调
failCallback = [];
resolve = (value) => {
// 如果状态不是等待 阻止程序向下执行
if (this.status !== PENDING) return;
// 将状态更改为成功
this.status = FULFILLED;
// 保存成功之后的值
this.value = value;
//判断成功回调是否存在
// if(this.failCallback)this.failCallback(this.value)
while (this.successCallback.length) this.successCallback.shift()(this.value);
};
reject = (reason) => {
// 如果状态不是等待 阻止程序向下执行
if (this.status !== PENDING) return;
// 将状态更改为失败
this.status = REJECTED;
// 保存失败后的原因
this.reason = reason;
//判断失败回调是否存在
while (this.failCallback.length) this.failCallback.shift()(this.value);
};
then = (successCallback, failCallback) => {
let promsie2 = new MyPromise((resolve, reject) => {
//判断状态
if (this.status === FULFILLED) {
setTimeout(() => {
let x = successCallback(this.value);
// 判断 x 的值是普通值还是promise对象
// 如果是普通值 直接调用resolve
// 如果是promise对象 查看promsie对象返回的结果
// 再根据promise对象返回的结果 决定调用resolve 还是调用reject
resolvePromoise(promsie2,x,resolve,reject)
},0)
} else if (this.status === REJECTED) {
setTimeout(() => {
let x = failCallback(this.reason);
// 判断 x 的值是普通值还是promise对象
// 如果是普通值 直接调用resolve
// 如果是promise对象 查看promsie对象返回的结果
// 再根据promise对象返回的结果 决定调用resolve 还是调用reject
resolvePromoise(promsie2,x,resolve,reject)
})
} else {
this.successCallback.push(successCallback);
this.failCallback.push(failCallback);
}
});
return promsie2;
};
}
function resolvePromoise(promsie2,x,resolve,reject){
// 判断当前then返回的Promise 和 回调中返回的是不是同一个Promise
if(promsie2 === x) { //promsie2 = then方法返回的Promise x =then方法中回调返回的promise
return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
}
if( x instanceof MyPromise){
//promise
x.then(resolve,reject )
}else{
//普通值
resolve(x)
}
}
Promise 捕获错误以及then链式调用其他状态代码补充
捕获错误
在上述的代码中我们并没有进行任何的错误处理的,为了程序的健壮醒,我们是需要捕获错误,处理错误的
- 执行器错误(当执行器中的代码在执行中发生错误)我们需要吧promise状态变成错误
constructor (executor) {
try {
executor(this.resolve, this.reject)
} catch (e) {
this.reject(e);
}
}
测试
let promise = new MyPromise((resolve, reject) => {
throw new Error("executor err")
// setTimeout(() => {
resolve("成功");
// }, 2000);
// reject("失败");
});
promise.then((success) => {
},(err)=>{
console.log(err.message); //executor err
})
捕获then方法的回调执行过程报错
- then方法回调方法执行过程中发生错误我们需要在下一个then方法中的错误回调捕获到
then = (successCallback, failCallback) => {
let promsie2 = new MyPromise((resolve, reject) => {
//判断状态
if (this.status === FULFILLED) {
setTimeout(() => {
try { //回调执行过程中错误捕获
let x = successCallback(this.value);
// 判断 x 的值是普通值还是promise对象
// 如果是普通值 直接调用resolve
// 如果是promise对象 查看promsie对象返回的结果
// 再根据promise对象返回的结果 决定调用resolve 还是调用reject
resolvePromoise(promsie2,x,resolve,reject)
} catch (error) {
reject(error)
}
},0)
} else if (this.status === REJECTED) {
setTimeout(() => {
try {//回调执行过程中错误捕获
let x = failCallback(this.reason);
// 判断 x 的值是普通值还是promise对象
// 如果是普通值 直接调用resolve
// 如果是promise对象 查看promsie对象返回的结果
// 再根据promise对象返回的结果 决定调用resolve 还是调用reject
resolvePromoise(promsie2,x,resolve,reject)
} catch (error) {
reject(error)
}
})
} else {
this.successCallback.push(successCallback);
this.failCallback.push(failCallback);
}
});
return promsie2;
};
演示
promise
.then((success) => {
throw new Error("第一个错误")
return 111
})
.then((value) => {
},(err)=>{
console.log(err); //第一个错误
});
异步操作
之前我们处理then链式回调的时候使用的是这种方式
this.successCallback.push(successCallback);
this.failCallback.push(failCallback);
但是随着我们不断增加功能,这种方式已经无法满足,在successCallback||failCallback是无法做异常捕获处理的,所以需要
this.successCallback.push(() => {
setTimeout(() => {
try {
//回调执行过程中错误捕获
let x = successCallback(this.value);
// 判断 x 的值是普通值还是promise对象
// 如果是普通值 直接调用resolve
// 如果是promise对象 查看promsie对象返回的结果
// 再根据promise对象返回的结果 决定调用resolve 还是调用reject
resolvePromoise(promsie2, x, resolve, reject);
} catch (error) {
reject(error);
}
}, 0);
});
this.failCallback.push(() => {
setTimeout(() => {
let x = failCallback(this.reason);
// 判断 x 的值是普通值还是promise对象
// 如果是普通值 直接调用resolve
// 如果是promise对象 查看promsie对象返回的结果
// 再根据promise对象返回的结果 决定调用resolve 还是调用reject
resolvePromoise(promsie2, x, resolve, reject);
});
});
then 方法实现参数变成可选参数
实现功能如下:
var promise=new Promise(()=>{
reject(1)
})
promise.then().then().then((value)=>{
console.log(value) //1
})
- 不传递参数等同于
.then(value => value)
所以我们需要在then 方法中做判断,这样我们就实现了
then = (successCallback, failCallback) => {
successCallback = successCallback ? successCallback: value => value
failCallback = failCallback ? failCallback: reason => reason
})
这样我们Promise的内部处理就全部基本完成了,完整代码如下
const PENDING = 'pending'; // 等待
const FULFILLED = 'fulfilled'; // 成功
const REJECTED = 'rejected'; // 失败
class MyPromise {
constructor (executor) {
try {
executor(this.resolve, this.reject)
} catch (e) {
this.reject(e);
}
}
// promsie 状态
status = PENDING;
// 成功之后的值
value = undefined;
// 失败后的原因
reason = undefined;
// 成功回调
successCallback = [];
// 失败回调
failCallback = [];
resolve = value => {
// 如果状态不是等待 阻止程序向下执行
if (this.status !== PENDING) return;
// 将状态更改为成功
this.status = FULFILLED;
// 保存成功之后的值
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()()
}
then (successCallback, failCallback) {
// 参数可选
successCallback = successCallback ? successCallback : value => value;
// 参数可选
failCallback = failCallback ? failCallback: reason => { throw reason };
let promsie2 = new MyPromise((resolve, reject) => {
// 判断状态
if (this.status === FULFILLED) {
setTimeout(() => {
try {
let x = successCallback(this.value);
// 判断 x 的值是普通值还是promise对象
// 如果是普通值 直接调用resolve
// 如果是promise对象 查看promsie对象返回的结果
// 再根据promise对象返回的结果 决定调用resolve 还是调用reject
resolvePromise(promsie2, x, resolve, reject)
}catch (e) {
reject(e);
}
}, 0)
}else if (this.status === REJECTED) {
setTimeout(() => {
try {
let x = failCallback(this.reason);
// 判断 x 的值是普通值还是promise对象
// 如果是普通值 直接调用resolve
// 如果是promise对象 查看promsie对象返回的结果
// 再根据promise对象返回的结果 决定调用resolve 还是调用reject
resolvePromise(promsie2, x, resolve, reject)
}catch (e) {
reject(e);
}
}, 0)
} else {
// 等待
// 将成功回调和失败回调存储起来
this.successCallback.push(() => {
setTimeout(() => {
try {
let x = successCallback(this.value);
// 判断 x 的值是普通值还是promise对象
// 如果是普通值 直接调用resolve
// 如果是promise对象 查看promsie对象返回的结果
// 再根据promise对象返回的结果 决定调用resolve 还是调用reject
resolvePromise(promsie2, x, resolve, reject)
}catch (e) {
reject(e);
}
}, 0)
});
this.failCallback.push(() => {
setTimeout(() => {
try {
let x = failCallback(this.reason);
// 判断 x 的值是普通值还是promise对象
// 如果是普通值 直接调用resolve
// 如果是promise对象 查看promsie对象返回的结果
// 再根据promise对象返回的结果 决定调用resolve 还是调用reject
resolvePromise(promsie2, x, resolve, reject)
}catch (e) {
reject(e);
}
}, 0)
});
}
});
return promsie2;
}
finally (callback) {
return this.then(value => {
return MyPromise.resolve(callback()).then(() => value);
}, reason => {
return MyPromise.resolve(callback()).then(() => { throw reason })
})
}
catch (failCallback) {
return this.then(undefined, failCallback)
}
static all (array) {
let result = [];
let index = 0;
return new MyPromise((resolve, reject) => {
function addData (key, value) {
result[key] = value;
index++;
if (index === array.length) {
resolve(result);
}
}
for (let i = 0; i < array.length; i++) {
let current = array[i];
if (current instanceof MyPromise) {
// promise 对象
current.then(value => addData(i, value), reason => reject(reason))
}else {
// 普通值
addData(i, array[i]);
}
}
})
}
static resolve (value) {
if (value instanceof MyPromise) return value;
return new MyPromise(resolve => resolve(value));
}
}
function resolvePromise (promsie2, x, resolve, reject) {
if (promsie2 === x) {
return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
}
if (x instanceof MyPromise) {
// promise 对象
// x.then(value => resolve(value), reason => reject(reason));
x.then(resolve, reject);
} else {
// 普通值
resolve(x);
}
}