Promise的原理以及手写一个Promise
观察原生promise用法,我们可以发现,在new Promise时候传入了一个函数,这个函数在规范中的叫法是exector 执行器
看到这里,我们先有一个大概思路,构建一个自己的Promise构造函数。
// 这里我们创建了一个构造函数 参数就是执行器
function Promise(exector) {
}好的,第一步完成, 重点来了,这个Promise内部到底干了什么呢 可以看到,原生的exector中传入了两个参数,第一个参数执行会让promise状态变为resolve, 也就是成功, 第二个执行会让函数变为reject状态,也就是失败
并且这连个形参执行之后都可以传入参数,我们继续完善代码 我们将这两个形参的函数封装在构造函数内部
// 这里我们创建了一个构造函数 参数就是执行器
function Promise(exector) {
// 这里我们将value 成功时候的值 reason失败时候的值放入属性中
let self = this;
this.value = undefined;
this.reason = undefined;
// 成功执行
function resolve(value) {
self.value = value;
}
// 失败执行
function reject(reason) {
self.reason = reason;
}
exector(resolve, reject);
}这里问题来了,我们知道,promise的执行过程是不可逆的,resolve和rejeact之间也不能相互转化, 这里,我们就需要加入一个状态,判断当前是否在pending过程,另外我们的执行器可能直接报错,这里我们也需要处理一下。// 这里我们创建了一个构造函数 参数就是执行器
function Promise(exector) {
// 这里我们将value 成功时候的值 reason失败时候的值放入属性中
let self = this;
// 这里我们加入一个状态标识
this.status = 'pending';
this.value = undefined;
this.reason = undefined;
// 成功执行
function resolve(value) {
// 判断是否处于pending状态
if (self.status === 'pending') {
self.value = value;
// 这里我们执行之后需要更改状态
self.status = 'resolved';
}
}
// 失败执行
function reject(reason) {
// 判断是否处于pending状态
if (self.status === 'pending') {
self.reason = reason;
// 这里我们执行之后需要更改状态
self.status = 'rejected';
}
}
// 这里对异常进行处理
try {
exector(resolve, reject);
} catch(e) {
reject(e)
}
}new Promise之后我们怎么去改变promise对象的状态呢, 通过原生的用法我们了解到,需要使用then方法, then方法分别指定了resolved状态和rejeacted状态的回调函数
那怎么知道使用哪个回调函数呢,我们刚不是在构造函数内部定义了status么,这里就用上啦,上代码
那怎么知道使用哪个回调函数呢,我们刚不是在构造函数内部定义了status么,这里就用上啦,上代码
// 我们将then方法添加到构造函数的原型上 参数分别为成功和失败的回调
Promise.prototype.then = function(onFulfilled, onRejected) {
// 获取下this
let self = this;
if (this.status === 'resolved') {
onFulfilled(self.value);
}
if (this.status === 'rejected') {
onRejected(self.reason);
}
}ok,我们现在可以自己运行试试let promise = new Promise((resolve, reject) => {
resolve("haha");
})
promise.then(data => {
console.log(data); //输出 haha
}, err=> {
console.log(err);
})
// 多次调用
promise.then(data => {
console.log(data); //输出 haha
}, err=> {
console.log(err);
})上面可以注意到, new Promise中的改变状态操作我们使用的是同步,那如果是异步呢,我们平时遇到的基本都是异步操作,该如何解决?
这里我们需要在构造函数中存放两个数组,分别保存成功回调和失败的回调
因为可以then多次,所以需要将这些函数放在数组中
代码如下:
// 这里我们创建了一个构造函数 参数就是执行器
function Promise(exector) {
// 这里我们将value 成功时候的值 reason失败时候的值放入属性中
let self = this;
// 这里我们加入一个状态标识
this.status = 'pending';
this.value = undefined;
this.reason = undefined;
// 存储then中成功的回调函数
this.onResolvedCallbacks = [];
// 存储then中失败的回调函数
this.onRejectedCallbacks = [];
// 成功执行
function resolve(value) {
// 判断是否处于pending状态
if (self.status === 'pending') {
self.value = value;
// 这里我们执行之后需要更改状态
self.status = 'resolved';
// 成功之后遍历then中成功的所有回调函数
self.onResolvedCallbacks.forEach(fn => fn());
}
}
// 失败执行
function reject(reason) {
// 判断是否处于pending状态
if (self.status === 'pending') {
self.reason = reason;
// 这里我们执行之后需要更改状态
self.status = 'rejected';
// 成功之后遍历then中失败的所有回调函数
self.onRejectedCallbacks.forEach(fn => fn());
}
}
// 这里对异常进行处理
try {
exector(resolve, reject);
} catch(e) {
reject(e)
}
}
// then 改造
Promise.prototype.then = function(onFulfilled, onRejected) {
// 获取下this
let self = this;
if (this.status === 'resolved') {
onFulfulled(self.value);
}
if (this.status === 'rejected') {
onRejected(self.reason);
}
// 如果异步执行则位pending状态
if(this.status === 'pending') {
// 保存回调函数
this.onResolvedCallbacks.push(() => {
onFulfilled(self.value);
})
this.onRejectedCallbacks.push(() => {
onRejected(self.reason)
});
}
}
// 这里我们可以再次实验
let promise = new Promise((resolve, reject) => {
setTimeout(() => {
if(Math.random() > 0.5) {
resolve('成功');
} else {
reject('失败');
}
})
})
promise.then((data) => {
console.log('success' + data);
}, (err) => {
console.log('err' + err);
})
我们都熟悉Jquery,它的链式调用是用rerun this来做的,可是这里却不行,原因文章末尾再解释。 我们采取返回一个新的promise对象来实现链式调用. 意思也就是p.then()返回一个新promise对象。
//给这个函数加个返回值,返回值就是一个新new的promise对象
Promise.prototype.then = function (onFulFilled, onRejected) {
let p2 = new Promise((resolve, reject) => {});
if (this.status === 'pending') {
this.onFulFilledCallbacks.push(() => {
onFulFilled(this.value)
});
this.onRejectedCallbacks.push(() => {
onRejected(this.reason)
})
} else if (this.status === 'resolved') {
onFulFilled(this.value);
} else if (this.status === 'rejected') {
onRejected(this.reason);
}
return p2;
}
- 加了两行代码,我们测试一下输出结果:
任务执行完了
第一个成功回调 - 根据上一次我们所讲的可以总结出如下结论:真正触发onFulFilledCallbacks里所存储的回调函数只有两个地方:
- resolve被调用的时候.(resolve是用户调用的,因为用户当然知道哪种逻辑算任务成功,哪种逻辑算任务失败,例如我们上一章的例子,随机数大于5我就认为是成功的.)
- then被调用的时候.(then也是用户调用的,promise里的任务执行完了后要做啥)