手写Promise这种文章应该已经烂大街了,不过为了加深自己的理解,还是决定写一篇文章,哈哈,有错误之处,恳请指出,相互学习。Promise/A+ 规范
1.搭建框架
先来看看原生promise实例的创建:
let promise=new Promise((resolve,reject)=>{
resolve(666)
})
promise.then((value)=>{
console.log(value)
},err=>{
console.log(err)
})
从上面我们可以看到,构造函数里面会传入一个函数,包括两个方法resolve,reject,这两个方法都是javascript内部提供给我们的,并不是我们自己写的。resolve方法在异步操作成功时执行,并将异步操作的结果作为参数传递出去;reject放在在异步操作失败的情况下执行,并将执行的错误信息作为参数传递出去。
Promise还提供了一个then的方法,通过打印promise实例可以看出,该方法是挂在原型对象上的。then方法里面可以接受两个回调函数作为参数。第一个为运行成功时的调用,第二个为失败时的调用。这两个函数是可选的。这两个函数的参数就是resolve,reject函数传出的值。
基于此,我们来搭建自己的Promise,把它命名为MyPromise,首先的大致框架如下,下面再一步步的往里面填:
const PENDING="pending"
const REJECTED="rejected"
const RESOLVED="resolved"
class MyPromise {
//构造函数
constructor(excutor){
this.value=null
this.status=PENDING
let resovle=(value)=>{
if(this.status===PENDING){
this.status=RESOLVED
this.value=value
}
}
let reject=(value)=>{
if(this.status===PENDING){
this.status=REJECTED
this.value=value
}
}
try {
excutor(resovle,reject)
} catch (error) {
reject(error)
}
}
then(onResolved,onRejected){
//status状态为resolved时,执行
if(this.status===RESOLVED){
onResolved(this.value)
}
//status状态为rejected时,执行
if(this.status===REJECTED){
onRejected(this.value)
}
}
}
2. 慢慢填坑
上面的then方法中,onResolved,onRejected可能传入的不是函数,或者没有传,此时我们需要对其判断一下,还有就是在执行onResolved,onRejected方法内部报错的时候,统一交给onRejected处理。所以上面的方法改造为:
then(onResolved, onRejected) {
if (typeof onResolved !== "function") {
onResolved = () => this.value;
}
if (typeof onRejected !== "function") {
onRejected = () => this.value;
}
if (this.status === RESOLVED) {
try {
onResolved(this.value);
} catch (error) {
onRejected(error);
}
}
if (this.status === REJECTED) {
try {
onRejected(this.value);
} catch (error) {
onRejected(error);
}
}
}
像这样改造之后,我们这样执行就会没有问题
promise.then().then(
value => console.log(value),
error => {}
);
下面我们来运行一下我们自己创建的这个MyPromise函数,试试效果怎么样:
可以看到,正确的打印了666,但是能不能处理异步代码呢,我们在试试
从上面的图,我们看到,啥都没有输出,那么就是不能处理异步代码。其实在上面的代码中当我们执行到then方法的时候,上面的setTimeout里的代码并没有执行,因为其是宏任务,会在下一个事件循环中去执行。在then方法中,并没有对status为pending时作处理,所以不会输出任何东西。要想正确处理,我们需要先保存执行函数,当status状态改变的时候再去执行相应的处理函数。
constructor(excutor) {
this.value = null;
this.status = PENDING;
this.callbacks=[]
let resovle = value => {
if (this.status === PENDING) {
this.status = RESOLVED;
this.value = value;
this.callbacks.map(func=>{
func.onResolved(value)
})
}
};
let reject = value => {
if (this.status === PENDING) {
this.status = REJECTED;
this.value = value;
this.callbacks.map(func=>{
func.onRejected(value)
})
}
};
try {
excutor(resovle, reject);
} catch (error) {
reject(error);
}
}
then(onResolved, onRejected) {
if (typeof onResolved !== "function") {
onResolved = () => this.value;
}
if (typeof onRejected !== "function") {
onRejected = () => this.value;
}
if(this.status===PENDING){
this.callbacks.push({
onResolved:(value)=>{
try {
onResolved(value)
} catch (error) {
onRejected(error)
}
},
onRejected:(value)=>{
try {
onRejected(value)
} catch (error) {
onRejected(error)
}
}
})
}
if (this.status === RESOLVED) {
try {
onResolved(this.value);
} catch (error) {
onRejected(error);
}
}
if (this.status === REJECTED) {
try {
onRejected(this.value);
} catch (error) {
onRejected(error);
}
}
}
此时在执行上面的代码,看到已经可以正确的执行:
我们都知道promise是一个微任务,他会在主线程同步代码执行完之后,再执行,那么我们看看我们这个呢
从上图可以看出,他和我们预期的并不一样,并不是异步的。我们可以借助setTimeout将onResolved,onRejected变成宏任务去执行。
if (this.status === RESOLVED) {
setTimeout(() => {
try {
onResolved(this.value);
} catch (error) {
onRejected(error);
}
});
}
if (this.status === REJECTED) {
setTimeout(() => {
try {
onRejected(this.value);
} catch (error) {
onRejected(error);
}
});
}
如上,已经达到了我们要的效果了,那么我们再来看看异步任务的时候是不是也能正确输出结果呢
结果又是错的呢,哈哈,其实到这里,大家应该都知道怎么处理了吧,和上面的一样,我们只需要把异步执行时收集起来的函数也放在setTimeout中执行,应该就可以,那我们就试试看吧。
constructor(excutor) {
this.value = null;
this.status = PENDING;
this.callbacks=[]
let resovle = value => {
if (this.status === PENDING) {
this.status = RESOLVED;
this.value = value;
setTimeout(()=>{
this.callbacks.map(func=>{
func.onResolved(value)
})
})
}
};
let reject = value => {
if (this.status === PENDING) {
this.status = REJECTED;
this.value = value;
setTimeout(()=>{
this.callbacks.map(func=>{
func.onRejected(value)
})
})
}
};
try {
excutor(resovle, reject);
} catch (error) {
reject(error);
}
}
可以看到,运行结果已经正确
3. 链式调用
我们都知道promise是可以链式调用的,其实也就是promise的then函数返回一个promise对象,把上一次的结果通过resolve或者reject函数返回出去,这样就可以达到链式调用的效果。
then(onResolved, onRejected) {
if (typeof onResolved !== "function") {
onResolved = () => this.value;
}
if (typeof onRejected !== "function") {
onRejected = () => this.value;
}
return new MyPromise((resolve,reject)=>{
if(this.status===PENDING){
this.callbacks.push({
onResolved:(value)=>{
try {
let result=onResolved(value)
resolve(result)
} catch (error) {
reject(error)
}
},
onRejected:(value)=>{
try {
let result=onRejected(value)
resolve(result)
} catch (error) {
reject(error)
}
}
})
}
if (this.status === RESOLVED) {
setTimeout(() => {
try {
let result=onResolved(this.value);
resolve(result)
} catch (error) {
reject(error)
}
});
}
if (this.status === REJECTED) {
setTimeout(() => {
try {
let result=onRejected(this.value);
resolve(result)
} catch (error) {
reject(error)
}
});
}
})
}
我们可以测试一下:
已经达到了链式调用的效果,但是在then函数中我们也可以再次返回Promise对象,这个试试会是啥效果呢,我们打印一下看看
而 原生的Promise的执行效果如下:
由此可知,我们必须对返回的类型进行判断,如果是Promise类型,我们需要对其执行then方法,把当前的resolve和reject方法传入即可,如果是其他类型则 执行直接resolve或者reject其值即可。分析清楚了之后,我们再一次修改代码如下:
then(onResolved, onRejected) {
if (typeof onResolved !== "function") {
onResolved = () => this.value;
}
if (typeof onRejected !== "function") {
onRejected = () => this.value;
}
return new MyPromise((resolve,reject)=>{
if(this.status===PENDING){
this.callbacks.push({
onResolved:(value)=>{
try {
let result=onResolved(value)
if(result instanceof MyPromise){
result.then(resolve,reject)
}else{
resolve(result)
}
} catch (error) {
reject(error)
}
},
onRejected:(value)=>{
try {
let result=onRejected(value)
if(result instanceof MyPromise){
result.then(resolve,reject)
}else{
resolve(result)
}
} catch (error) {
reject(error)
}
}
})
}
if (this.status === RESOLVED) {
setTimeout(() => {
try {
let result=onResolved(this.value);
if(result instanceof MyPromise){
result.then(resolve,reject)
}else{
resolve(result)
}
} catch (error) {
reject(error)
}
});
}
if (this.status === REJECTED) {
setTimeout(() => {
try {
let result=onRejected(this.value);
if(result instanceof MyPromise){
result.then(resolve,reject)
}else{
resolve(result)
}
} catch (error) {
reject(error)
}
});
}
})
}
再把上面的测试代码执行一遍,发现已经得到我们要的效果
写到这里其实已经差不多了,还有一点就是then方法返回的promise对象不能是then相同的promise,会报循环引用的错误,我们来看下原生的promise对象的执行效果如何,如下图:
那么我们需要对返回的promise进行判断就可以
let result = onResolved(value);
if (result === promise) {
throw new TypeError("Chaining cycle detected for promise");
}
最后我们再来执行一下代码看看:
已经达到我们想要的结果
好了,分析就到这里,如果错误的地方,欢迎指出来,一起学习进步!
下面贴出完整的代码:
const PENDING = "pending";
const REJECTED = "rejected";
const RESOLVED = "resolved";
class MyPromise {
//构造函数
constructor(excutor) {
this.value = null;
this.status = PENDING;
this.callbacks = [];
let resovle = value => {
if (this.status === PENDING) {
this.status = RESOLVED;
this.value = value;
setTimeout(() => {
this.callbacks.map(func => {
func.onResolved(value);
});
});
}
};
let reject = value => {
if (this.status === PENDING) {
this.status = REJECTED;
this.value = value;
setTimeout(() => {
this.callbacks.map(func => {
func.onRejected(value);
});
});
}
};
try {
excutor(resovle, reject);
} catch (error) {
reject(error);
}
}
then(onResolved, onRejected) {
if (typeof onResolved !== "function") {
onResolved = () => this.value;
}
if (typeof onRejected !== "function") {
onRejected = () => this.value;
}
let promise = new MyPromise((resolve, reject) => {
if (this.status === PENDING) {
this.callbacks.push({
onResolved: value => {
try {
let result = onResolved(value);
if (result === promise) {
throw new TypeError("Chaining cycle detected for promise");
}
if (result instanceof MyPromise) {
result.then(resolve, reject);
} else {
resolve(result);
}
} catch (error) {
console.log(error);
reject(error);
}
},
onRejected: value => {
try {
let result = onRejected(value);
if (result === promise) {
throw new TypeError("Chaining cycle detected for promise");
}
if (result instanceof MyPromise) {
result.then(resolve, reject);
} else {
resolve(result);
}
} catch (error) {
console.log(error);
reject(error);
}
}
});
}
if (this.status === RESOLVED) {
setTimeout(() => {
try {
let result = onResolved(this.value);
if (result === promise) {
throw new TypeError(" Chaining cycle detected for promise");
}
if (result instanceof MyPromise) {
result.then(resolve, reject);
} else {
resolve(result);
}
} catch (error) {
console.log(error);
reject(error);
}
});
}
if (this.status === REJECTED) {
setTimeout(() => {
try {
let result = onRejected(this.value);
if (result === promise) {
throw new TypeError("Chaining cycle detected for promise");
}
if (result instanceof MyPromise) {
result.then(resolve, reject);
} else {
resolve(result);
}
} catch (error) {
console.log(error);
reject(error);
}
});
}
});
return promise;
}
}
let promise = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve(666);
console.log("promise")
});
});
promise.then(
value => {
console.log(value);
return 888;
},
err => {
console.log(err);
}
).then()
.then(value=>{
console.log(value)
})
console.log("main")
补充更新,上面是实现了一个基本的Promise,下面补充几个静态方法的实现:
- resolve
- reject
- all
- race
static resolve(value) {
return new MyPromise((resolve, reject) => {
if (value instanceof MyPromise) {
value.then(resolve, reject);
} else {
resolve(value);
}
});
}
static reject(value) {
return new MyPromise((resolve, reject) => {
reject(value);
});
}
static all(promises) {
let resolves = [];
return new MyPromise((resolve, reject) => {
promises.forEach((p,index) => {
MyPromise.resolve(p).then( //1.修正p.then
(value) => {
resolves[index]=value //2.修正 resolves.push(value)
if (resolves.length === promises.length) {
resolve(resolves);
}
},
(error) => {
reject(error);
}
);
});
});
}
static race(promises){
return new MyPromise((resolve,reject)=>{
promises.map(p=>{
p.then(value=>{
resolve(value)
})
})
})
}
ps:修正promise.all方法,第一处的修正是应为传入中的参数不一定都是promise对象,可能是个普通值,所以需要用promise.resolve转换一下;第二处是为了保证返回的结果与传入参数的顺序一致