目录
1.前言
手写一版promise不仅仅是为了面试什么的,了解了基本的实现机制,在工作使用中也会更加得心应手。在看了手写promise的博客以后,第一感觉就是太长,代码太多不想看。我想大多数同学肯定也像我一样看到第一眼可能就被劝退了,或者狠下心想认真看完,到最后发现一知半解。。。
不要再犹豫了,相信我,看什么看几遍都不如自己从头到尾的实现一遍记忆深刻,一遍不够那么就两遍,总有一遍能让你有到豁然开朗的感觉。可能有的小伙伴看到这里又想溜了。但我只是给大家梳理一下手写的流程,具体理解还要靠大家多手敲啊!!
(点我)为了在博客更加清晰,具体代码(带注释)大家可以点击对照练习
2.准备工作
1.须知
1)想实现一个东西的原理的前提是,你很了解他,知道他的所有特征,不然的话你连他是做什么的,怎么用都不知道,又何谈实现呢,所以如果对promise不熟悉的话还是先不建议手写。
2)要开始写代码之前要先想清楚promise的所有的方法,以及使用方法,然后再搭建架子,一步步实现。
3)我们知道promise使用时都是当做一个类,或者说一个构造函数使用的,并且还存在很多静态方法和原型上的方法,比如原型上的 then
、catch
,类上的静态方法比如 resolve
、reject
、all
、reace
4)所以我们的按照ES5的写法搭建基本架子就如下:
2.搭建架子
(function () {
// promise 就是构造函数
// new Promise((resolve,reject)=>{.....})
const PENDING = 'pending'; // 定义维护的状态机
const REJECTED = 'rejected';
const RESOLVED = 'resolved';
function Promise(excutor) {
this.status = PENDING;
this.data = undefined;
this.callbacks = []; // 用来存储回调函数 {onResolved,onRejected}
}
Promise.prototype.then = function (onResolved, onRejected) { // 传入两个会回调
}
Promise.prototype.catch = function (onRejected) {
}
Promise.resolve = function (value) {
}
Promise.reject = function (reason) {
}
Promise.all = function (promises) {
}
Promise.reace = function (promises) {
}
})()
3.逐个突破
1.resolve,reject
1)我们根据一般的使用习惯进行逐步的实现,在使用时我们一般都是通过new Promise()
传入一个执行函数,执行通过调用执行函数的参数方法来改变状态,并且执行then
中的回调。所以在这个过程中最重要的部分就是传来的 resolve
,reject
这两个函数了。
2)首先要理解这两个函数的作用,第一就是改变状态,第二就是执行回调,并且状态只能改变一次,以resolve为例。reject同理。
if (this.status !== PENDING) {
return;
}
this.status = RESOLVED;
this.data = value; // 存储value值
if (this.callbacks.length > 0) {
setTimeout(() => {
this.callbacks.forEach(callbacksObj => {
callbacksObj.onResolved(value);
});
});
}
// 我这里将then回调函数存储为对象的形式了
// {onResolved(){},onRejected(){}} 然后遍历执行
3)这里需要理解的就是这个this.data = value;
,为什么需要存储这个值呢?不是调用resolve(value)
的时候传入value
就可以直接传给onResolved(value)
了吗?
因为promise
执行函数可能是同步的,比如:
new Promise((reslove,reject)=>{
reslove(1);
}).then(value=>{
console.log(value)
})
这时当执行reslove
的时候,还没有执行到then()
也就是说还没有回调函数被存储。那到时候回调函数想执行的时候value
从哪里获取呢?这时这个data
就出场了
2.接下来就是then方法的实现了
1)then
方法是整个promise
实现中比较难理解的部分,难理解就在于它的链式传递和它返回值为promise
的问题,如果理解了这两个部分,实现起来还是比较容易
2)在实现时我们首先要知道then方法的使用规则,首先then() 传入两个回调,一个成功的回调,一个失败的回调,并且可以链式调用,也就是说可以then(()=>{},()=>{}).then(()=>{},()=>{})
如此调用下去,之所以可以是因为then方法返回的是promise。
3)既然返回的是promise
那就有成功和失败的状态,此promise
的状态取决于,回调函数的返回值
let p = new Promise((reslove, reject) => {
reslove();
});
如果then返回promise 那么then方法的成功与否取决于此promise
p.then(value => {
return Promise.resolve(1)
}).then(value => {
console.log(value);
}); // 打印为 1
如果为值类型,返回什么就是成功的并且成功的value为此返回值,不返回也是成功的,只不过值为undefined
p.then(value => {
return 1;
}).then(value => {
console.log(value)
}); // 打印为 1 说明是成功的
p.then(value => {
console.log('不返回')
}).then(value => {
console.log(value)
}); // 打印为 undefined
如果then中抛出异常,那么这个then返回的promise就是失败的。
p.then(value => {
throw new Error();
}).then(value => {
},reason => {
console.log("出错了!")
}); // 打印为 出错了!
4)接下来需要主要的就是如果then( ),传递的两个回调函数不为函数类型如何处理
let p = Promise.resolve(2)
p.then(12,()=>{}).then(value => {
console.log(value)
}); // 输出为2
可以看到,既然可以把value传递给下一个then,说明这个不为函数的回调一定是被处理为了 value => value
也就是封装成了一个返回value
的函数,我们又知道如果返回值类型,相当于返回了一个Promise.resolve(value)
5)所以then的架子就是
Promise.prototype.then = function (onResolved, onRejected) { // 传入两个会回调
// 如果为值类型也是会继续传递的
onResolved = typeof onResolved === 'function' ? onResolved : value => value;
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason };
// 返回的promise
return new Promise((resolve, reject) => {
if (this.status === PENDING) {
this.callbacks.push({
onResolved(value) {
// 写用来判断返回的promise成功与否的逻辑
},
onRejected(reason) {
// 写用来判断返回的promise成功与否的逻辑
}
})
} else if (this.status === RESOLVED) {
// 在执行函数为同步函数的话,因为resolve执行时的时候没有callback函数
// 所以到这里then方法就要直接执行
// 此时value从哪里获取呢 就是上面的this.data
setTimeout(() => {
// 写用来判断返回的promise成功与否的逻辑
});
} else { // 状态为rejected
setTimeout(() => {
// 写用来判断返回的promise成功与否的逻辑
});
}
})
}
判断promise的成功与否的逻辑我那一个举例,其他都一致
else if (this.status === RESOLVED) { // 就相当于resolve的作用 因为resolve的时候没有callback函数 所以到这里要直接执行
setTimeout(() => {
try {
const result = onResolved(this.data);
if (result instanceof Promise) { // 判断如果为promise类型,那么状态就有此promise决定
result.then(
value => resolve(value),
reason => reject(reason)
)
} else { // 如果为普通值类型
resolve(result);
}
} catch (error) { // 如果报错
reject(error)
}
});
3.catch
实现了then catch就简单多了,直接复用逻辑
Promise.prototype.catch = function (onRejected) {
return this.then(undefined, onRejected);
}
4.静态方法 resolve 、 reject
这里比较简单,就直接相当于工厂方法,返回一个成功的Promise实例,或者失败的Promise实例就好了。
以reject为例
Promise.reject = function (reason) {
return new Promise((resolve, reject) => {
reject(reason);
})
}
5. all 、reace
这两个方法也比较容易,首先要明确的就是,他们返回的都是Promise,并且Promise的成功与失败由数组中的promise决定。
以all方法为例:只需记录当前成功的数量,如果为传入数组的长度就返回成功,否则有失败就为失败。
Promise.all = function (promises) {
const values = [];
let count = 0; // 记录成功的个数
return new Promise((reslove, reject) => {
promises.forEach(peomise => {
peomise.then((value) => {
values.push(values);
count++;
}, (reason) => {
reject(reason)
});
});
if (count === promises.length) {
reslove(values)
}
})
}
6.结束语
到这里我们的promise就结束了,我希望同学们可以按照心里的这个框架,随便找一个写好的跟着写几遍,或者跟着我的写几遍,相信你一定可以理解的很透彻。
如果有错误还请及时指出,以免误解其他同学,以上仅为个人感受,如果有更好的学习方法还请多交流。