Promise的一些知识和用法都讲了,此时就要来看看我们是否能够手写一个Promise构造函数出来,这样更有助于帮助我们理解Promise,在某些面试中也能过用得到。
初始结构搭建
这里我们借助构造函数的方法去搭建Promise的基础结构,因为Promise本身就是一个构造函数,当然也可以采用ES6里面的类去搭建。
js
function MyPromise() {}
const p = new MyPromise((resolve, reject) => {});
接下来封装resolve
函数和reject
函数。我们在new Promise
的时候会传一个执行函数 (resolve, reject) => {}
, 我们就用形参executor
去接收,因为Promise的执行函数会在new
的过程中同步执行,所以这里我们也要去直接调用这个形参executor
。
js
function MyPromise(executor) {
executor();
}
const p = new MyPromise((resolve, reject) => {});
执行器函数(resolve, reject) =>{}
有两个参数,所以executor
也得有两个参数executor(resolve,reject)
,但是这样会报错,因为resolve
和reject
没有声明。
js
function MyPromise(executor) {
executor(resolve, reject);
}
const p = new MyPromise((resolve, reject) => {});
所以接下来我们就要去声明这个resolve
函数和reject
函数,而且我们知道在调用这个resolve
函数和reject
函数的时候是可以传参的,所以我们还要用个形参去接收。
js
function MyPromise(executor) {
function resolve(data) {}
function reject(reason) {}
executor(resolve, reject);
}
const p = new MyPromise((resolve, reject) => {});
这样基本的初始结构就搭建好了,接下来要去手写resolve
函数和reject
函数。
resolve
函数和reject
函数搭建
处理状态和结果
resolve
函数会改变对象的状态和设置对象的结果值,所以首先先去添加对象状态属性和对象结果属性,状态初始为pending
,结果初始为undefined
,这都是前面讲的内容。
js
function MyPromise(executor) {
this.PromiseState = "pending";
this.PromiseResult = undefined;
function resolve(data) {}
function reject(reason) {}
executor(resolve, reject);
}
const p = new MyPromise((resolve, reject) => {});
现在手写resolve
函数的功能,去修改状态,然后给结果赋值。这里我们不能直接this.PromiseState = "fulfilled"
,看下面代码,resolve
函数是直接调用的,这时候this
指向的是全局,取不到PromiseState
。
js
let p = new Promise((resolve,reject) => {
resolve()
})
所以我们要在外面定义一个const self = this
去保存这个this
,然后self.PromiseState = "fulfilled"
,当然用bind
方法也行。
js
function MyPromise(executor) {
this.PromiseState = "pending";
this.PromiseResult = undefined;
// 保存this,方便后面resolve取值
const self = this;
function resolve(data) {
self.PromiseState = "fulfilled";
self.PromiseResult = data;
}
function reject(reason) {}
executor(resolve, reject);
}
const p = new MyPromise((resolve, reject) => {});
reject
函数同理去操作。
js
function MyPromise(executor) {
this.PromiseState = "pending";
this.PromiseResult = undefined;
const self = this;
function resolve(data) {
self.PromiseState = "fulfilled";
self.PromiseResult = data;
}
function reject(reason) {
self.PromiseState = "rejected";
self.PromiseResult = reason;
}
executor(resolve, reject);
}
const p = new MyPromise((resolve, reject) => {});
处理异常
除了上述修改状态和结果,我们还要考虑到抛出异常的操作,要处理异常就要用到try...catch
,现在主要是想一下应该加在哪里。看下面代码:
js
let p = new Promise((resolve, reject) => {
throw "异常";
});
从上面可以得知抛出异常的代码是在执行器函数里执行的,所以我们应该把try...catch
包裹在executor(resolve, reject)
上,抛出异常以后状态就变为rejected
,结果值就为抛出的异常,所以在catch
里去调用reject
函数,并且把抛出的异常传给reject
函数。
js
function MyPromise(executor) {
this.PromiseState = "pending";
this.PromiseResult = undefined;
const self = this;
function resolve(data) {
self.PromiseState = "fulfilled";
self.PromiseResult = data;
}
function reject(reason) {
self.PromiseState = "rejected";
self.PromiseResult = reason;
}
// 在executor(resolve, reject)外包裹一层try...catch
try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
const p = new MyPromise((resolve, reject) => {});
设置状态只能改变一次
我们知道Promise实例对象的状态只能修改一次,所以我们需要再resolve
函数和reject
函数里面加一个判断,如果状态不是pending
,就不进行操作。
js
function MyPromise(executor) {
this.PromiseState = "pending";
this.PromiseResult = undefined;
const self = this;
function resolve(data) {
// 添加判断状态只能修改一次
if (self.PromiseState !== "pending") return;
self.PromiseState = "fulfilled";
self.PromiseResult = data;
}
function reject(reason) {
// 添加判断状态只能修改一次
if (self.PromiseState !== "pending") return;
self.PromiseState = "rejected";
self.PromiseResult = reason;
}
try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
const p = new MyPromise((resolve, reject) => {});
至此,resolve
函数和reject
函数的搭建基本就完成了。
then方法的搭建
给MyPromise的原型上添加静态方法then
方法,then方法有两个函数参数onResolved
和onRejected
。
js
MyPromise.prototype.then = function (onResolved, onRejected) {};
then
方法里面有两个函数参数,所以我们需要一个判断去确定应该执行哪一个函数。而这个判断就是判断p
的状态是fulfilled
还是rejected
,因为then
方法是由p
调用的,所以p.then
里的this
指向的就是p
,我们就可以通过this.PromiseState
去判断。
而且调用then
方法的参数函数的时候,这个参数函数也是有一个参数的,这个参数就是实例对象的PromiseResult
,我们也要传过去。
js
MyPromise.prototype.then = function (onResolved, onRejected) {
if (this.PromiseState === "fulfilled") {
onResolved(this.PromiseResult);
}
if (this.PromiseState === "rejected") {
onRejected(this.PromiseResult);
}
};
我们现在来试一下基本的功能能不能实现:
js
function MyPromise(executor) {
this.PromiseState = "pending";
this.PromiseResult = undefined;
const self = this;
function resolve(data) {
if (self.PromiseState !== "pending") return;
self.PromiseState = "fulfilled";
self.PromiseResult = data;
}
function reject(reason) {
if (self.PromiseState !== "pending") return;
self.PromiseState = "rejected";
self.PromiseResult = reason;
}
try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
MyPromise.prototype.then = function (onResolved, onRejected) {
if (this.PromiseState === "fulfilled") {
onResolved(this.PromiseResult);
}
if (this.PromiseState === "rejected") {
onRejected(this.PromiseResult);
}
};
const p1 = new MyPromise((resolve, reject) => {
resolve("ok");
});
p1.then((res) => {
console.log(res);
});
const p2 = new MyPromise((resolve, reject) => {
reject("no ok");
});
p2.then(
(res) => {},
(error) => {
console.log(error);
}
);
由此可见,基本的功能是可以实现的了。
异步任务回调的执行
前面实现的是同步任务的操作,执行器函数里面的代码是同步任务,这个时候功能是可以实现的,但是如果执行的是异步任务,还能够正常实现吗?
js
const p = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve("ok");
}, 1000);
});
p.then((res) => {
console.log(res);
});
由结果来看,如果执行函数里面执行的是异步任务的话,功能无法实现。
这是因为上面代码执行函数里是异步操作,一秒钟以后才会执行resolve
函数,所以JS按照顺序执行到then
方法的时候PromiseState
还是pending
,而我们对then
方法的搭建中,只有对PromiseState
是fulfilled
或者rejected
的判断然后去执行对应的后续处理,不会对PromiseState
是pending
有操作,而当一秒钟以后状态改变了,JS也早就过了then
方法的执行了,不会再回过头再去执行一次then
方法,所以我们需要对PromiseState
是pending
加上一个判断。
js
MyPromise.prototype.then = function (onResolved, onRejected) {
if (this.PromiseState === "fulfilled") {
onResolved(this.PromiseResult);
}
if (this.PromiseState === "rejected") {
onRejected(this.PromiseResult);
}
// 添加对状态为pending的判断
if (this.PromiseState === "pending") {
}
};
那这个判断里面该进行哪些操作呢?首先,我们需要知道执行函数里面执行的是异步任务还是同步任务,所以我们去改造一下Promise的构造函数,给其添加一个属性callback
。
js
function MyPromise(executor) {
this.PromiseState = "pending";
this.PromiseResult = undefined;
// 添加一个callback属性
this.callback = {};
const self = this;
function resolve(data) {
if (self.PromiseState !== "pending") return;
self.PromiseState = "fulfilled";
self.PromiseResult = data;
}
function reject(reason) {
if (self.PromiseState !== "pending") return;
self.PromiseState = "rejected";
self.PromiseResult = reason;
}
try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
const p = new MyPromise((resolve, reject) => {});
然后对在then
方法里的this.PromiseState === "pending"
判断中给callback
赋值,把then
方法的两个函数参数赋值给callback
。
js
MyPromise.prototype.then = function (onResolved, onRejected) {
if (this.PromiseState === "fulfilled") {
onResolved(this.PromiseResult);
}
if (this.PromiseState === "rejected") {
onRejected(this.PromiseResult);
}
// 如果状态为pending,就把then方法的两个函数参数赋值给callback
if (this.PromiseState === "pending") {
this.callback = {
onResolved,
onRejected,
};
}
};
这样我们就可以知道执行函数里面执行的是异步任务还是同步任务了,因为只要callback
里有值,就说明执行到then
方法的时候状态为pending
,就说明任务是异步任务,如果没有值,就说明是同步任务。
我们就可以在调用完resolve
函数或者reject
函数的时候加一个判断,如果callback
有值,就说明是异步任务,这个时候已经不会再去执行then
方法了,但是前面在执行then
方法状态为pending
的时候,就已经把then
方法的两个函数参数onResolved
和onRejected
赋值给callback
了,所以我们可以直接调用callback
里的onResolved
函数或者onRejected
函数,就跟调用then
方法是一样的了。
js
function MyPromise(executor) {
this.PromiseState = "pending";
this.PromiseResult = undefined;
this.callback = {};
const self = this;
function resolve(data) {
if (self.PromiseState !== "pending") return;
self.PromiseState = "fulfilled";
self.PromiseResult = data;
// 执行完以后通过callback判断是否为异步任务,是的话直接调用callback里对应的函数
if (self.callback.onResolved) {
self.callback.onResolved(data);
}
}
function reject(reason) {
if (self.PromiseState !== "pending") return;
self.PromiseState = "rejected";
self.PromiseResult = reason;
// 执行完以后通过callback判断是否为异步任务,是的话直接调用callback里对应的函数
if (self.callback.onRejected) {
self.callback.onRejected(reason);
}
}
try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
const p = new MyPromise((resolve, reject) => {});
到这里关于异步任务执行的回调就基本上完成了。
指定多个任务
Promise经常会有指定多个回调函数的情况,我们这里分两种情况,一种是执行函数里执行的是同步任务,另外一种是执行函数里执行的是异步任务。
执行函数里执行的是同步任务的情况:
js
function MyPromise(executor) {
this.PromiseState = "pending";
this.PromiseResult = undefined;
this.callback = {};
const self = this;
function resolve(data) {
if (self.PromiseState !== "pending") return;
self.PromiseState = "fulfilled";
self.PromiseResult = data;
if (self.callback.onResolved) {
self.callback.onResolved(data);
}
}
function reject(reason) {
if (self.PromiseState !== "pending") return;
self.PromiseState = "rejected";
self.PromiseResult = reason;
if (self.callback.onRejected) {
self.callback.onRejected(reason);
}
}
try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
MyPromise.prototype.then = function (onResolved, onRejected) {
if (this.PromiseState === "fulfilled") {
onResolved(this.PromiseResult);
}
if (this.PromiseState === "rejected") {
onRejected(this.PromiseResult);
}
if (this.PromiseState === "pending") {
this.callback = {
onResolved,
onRejected,
};
}
};
const p = new MyPromise((resolve, reject) => {
resolve("ok");
});
// 同步任务指定多个回调函数
p.then((res) => {
console.log(1);
});
p.then((res) => {
console.log(2);
});
可以看到p
状态改变以后,then
方法会按照顺序去一个一个执行,所以功能是可以实现的。
执行函数里执行的是异步任务的情况:
js
const p = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve("ok");
}, 1000);
});
p.then((res) => {
console.log(1);
});
p.then((res) => {
console.log(2);
});
可以看到,当执行函数里的任务是异步任务的时候,调用多个回调函数,只会执行最后一个回调函数。
这是因为两个p.then
都走了PromiseState === "pending"
这个判断,第一个p.then
给callback
赋值了以后,第二个p.then
也给callback
赋值了,就直接把callback
原本的值给覆盖了,所以就只执行了最后一个回调函数。
所以我们需要把这些回调函数都保存起来,那么callback
就不能是一个对象,而是一个数组,所以将callback
改成callbacks
。p.then
走PromiseState === "pending"
这个判断的时候直接给callbacks
数组去push,当然resolve
函数和reject
函数也要做对应的修改。
js
function MyPromise(executor) {
this.PromiseState = "pending";
this.PromiseResult = undefined;
// 将callback由对象改为callbacks数组
this.callbacks = [];
const self = this;
function resolve(data) {
if (self.PromiseState !== "pending") return;
self.PromiseState = "fulfilled";
self.PromiseResult = data;
// 对应的修改,callbacks遍历去调用所有的回调函数
self.callbacks.forEach((item) => {
// 判断有没有对应的后续处理
item.onResolved && item.onResolved(data);
});
}
function reject(reason) {
if (self.PromiseState !== "pending") return;
self.PromiseState = "rejected";
self.PromiseResult = reason;
// 对应的修改,callbacks遍历去调用所有的回调函数
self.callbacks.forEach((item) => {
// 判断有没有对应的后续处理
item.onRejected && item.onRejected(reason);
});
}
try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
MyPromise.prototype.then = function (onResolved, onRejected) {
if (this.PromiseState === "fulfilled") {
onResolved(this.PromiseResult);
}
if (this.PromiseState === "rejected") {
onRejected(this.PromiseResult);
}
if (this.PromiseState === "pending") {
// 直接让callbacks数组去push,多个回调函数都放进callbacks里
this.callbacks.push({
onResolved,
onRejected,
});
}
};
const p = new MyPromise((resolve, reject) => {});
此时再调用多个回调函数,功能就可以正常实现了。
js
const p = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve("ok");
}, 1000);
});
p.then((res) => {
console.log(1);
});
p.then((res) => {
console.log(2);
});
指定多个回调函数的功能也基本实现。
同步任务修改then方法的返回值
Promise其中的链式调用很重要,而要想实现链式调用then
方法必须得是返回一个Promise实例对象,现在我们就来完成这个功能。
then
方法要返回一个Promise实例对象,我们就在then
方法里去return
一个Promise实例对象:
js
MyPromise.prototype.then = function (onResolved, onRejected) {
// 在原本的操作外面包裹一层 new MyPromise
return new MyPromise((resolve, reject) => {
// 这里是原本的操作
if (this.PromiseState === "fulfilled") {
onResolved(this.PromiseResult);
}
if (this.PromiseState === "rejected") {
onRejected(this.PromiseResult);
}
if (this.PromiseState === "pending") {
this.callbacks.push({
onResolved,
onRejected,
});
}
});
};
不过这里返回的Promise实例对象状态为pending
,结果为undefined
,还需要再处理一下。
then方法返回的三种情况
前几篇文章说了,then
方法的返回结果有三种情况:
第一个是没有相关后续处理,后边的任务和前面保持一致。就是说p
的状态改变以后,p.then
方法没有对应的回调函数去处理,那么一来p.then
返回的实例对象newP
就跟p
保持一致。
js
let p = new Promise((resolve, reject) => {
resolve("123");
});
let newP = p.then(null, (res) => {});
console.log(newP);
第二个是有相关后续处理,但是还没有执行,新任务挂起。就是说p
有后续处理,但是p
还没有执行结束,也就是状态还未改变,此时不会调用对应的回调函数,所以叫做还没有执行。此时的newP
的状态就是pending
,结果为undefined
。
js
let p = new Promise((resolve, reject) => {
setTimeout(() => {
reject("123");
}, 3000);
});
let newP = p.then(null, (res) => {});
console.log(newP);
第三个就是有相关后续处理,并且已经执行,这个又会根据执行结果分别有好几种情况:
- 回调函数抛出异常:
newP
的状态就是rejected
,结果就是抛出去的异常。 - 回调函数
return
一个非Promise实例对象:newP
的状态就是fulfilled
,结果就是return
出去的值。 - 回调函数不
return
,也不抛出异常:参考第二个,看成return
出去一个undefined
,newP
的状态就是fulfilled
,结果就是undefined
。 - 回调函数
return
一个Promise实例对象:那么newP
的状态和结果就和这个return
出去的Promise实例对象相同。
第一种情况和第二种情暂时不用管,我们这里主要是考虑第三种情况,然后根据第三种情况的回调函数不同的执行结果去处理。
第三种情况其实主要就是分为两种,一种return
的情况,一种是抛出异常的情况。
搭建return的情况
首先我们要获取回调函数的执行结果result
,然后对其判断是否为Promise实例对象。
如果result
为Promise实例对象,就要返回相同状态和结果的实例对象,我们可以在里面去调用then方法
,如果result
状态是成功的就调用resolve(v)
,v
就是result
的结果,就相当于return new MyPromise((resolve,reject) => {resolve(v)})
,如果result
状态是失败的就调用reject(r)
,r
就是result
的结果,就相当于return new MyPromise((resolve,reject) => {reject(r)})
,这样一来就可以返回跟result
相同状态和结果的实例对象了。
有人可能会说为什么不直接返回这个result
呢,别忘了外面还有一层return new MyPromise((resolve, reject) => {})
呢。如果跨出这一层在这个外面去判断又多少有点麻烦了。
js
MyPromise.prototype.then = function (onResolved, onRejected) {
return new MyPromise((resolve, reject) => {
if (this.PromiseState === "fulfilled") {
// 拿到执行结果,这个result就是回调函数的返回值,如果没有返回值result就为undefined
let result = onResolved(this.PromiseResult);
// 对其判断是否为Promise实例对象
if (result instanceof MyPromise) {
// 如果是Promise实例对象,就说明回调函数执行结果是return一个实例对象,那么我们应该返回跟这个实例对象相同状态和结果的实例对象
// 可以去调用then方法,如果回调函数执行结果return的是成功状态的实例对象
// 就会执行resolve(v),这样就相当于 return new MyPromise((resolve,reject) => {resolve(v)}) v 就是result实例对象的结果,这样一来就返回result相同状态和结果的实例对象了
// 如果回调函数执行结果return的是失败状态的实例对象,就调用reject(r)也是一样的
// 注意不要被这个then方法迷住了,这个then方法不要去考虑我们外面嵌套的`return new MyPromise((resolve, reject) => {})`,只要去处理resolve(v)就好了,又不是取返回值,反正我是迷住了
result.then(
(v) => {
resolve(v);
},
(r) => {
reject(r);
}
);
} else {
// 如果不是Promise对象,then返回的Promise状态就是fulfilled,值就是result
// 这样就相当于 return new MyPromise((resolve,reject) => {resolve(result)}) 返回的实例对象状态为fulfilled,结果为result
resolve(result);
}
}
// 这边也是一样
if (this.PromiseState === "rejected") {
let result = onRejected(this.PromiseResult);
if (result instanceof MyPromise) {
result.then(
(v) => {
resolve(v);
},
(r) => {
reject(r);
}
);
} else {
resolve(result);
}
}
if (this.PromiseState === "pending") {
this.callbacks.push({
onResolved,
onRejected,
});
}
});
};
搭建抛出异常的情况
讲完了return
的情况,现在就来讲讲抛出异常的情况,抛出异常肯定是用try...catch
去处理。跟我们讲搭建resolve
函数的时候一样,现在要考虑这个该放在哪里。
看下面这段代码我们可以知道抛出异常的代码是执行在状态改变以后的后续操作,可能是状态成功执行onResolved
函数的时候也可能是状态失败执行onRejected
函数的时候。
js
// 状态成功
p.then(
() => {
throw "抛出错误";
},
() => {}
);
// 状态失败
p.then(
() => {},
() => {
throw "抛出错误";
}
);
所以这个try...catch
应该放在我们自己定义的then
方法中判断完状态准备执行后续操作的时候。我们知道处理异常就是返回一个失败状态的Promise实例对象,结果就是抛出去的异常,所以直接在catch
里调用reject
函数。
js
MyPromise.prototype.then = function (onResolved, onRejected) {
return new MyPromise((resolve, reject) => {
if (this.PromiseState === "fulfilled") {
// 放在这里,刚判断完状态的地方,把这个if语句下的代码段都包裹住
try {
let result = onResolved(this.PromiseResult);
if (result instanceof MyPromise) {
result.then(
(v) => {
resolve(v);
},
(r) => {
reject(r);
}
);
} else {
resolve(result);
}
} catch (e) {
// 如果抛出异常,就是返回一个失败状态的Promise实例对象,结果就是抛出的异常,所以直接调用reject函数
reject(e);
}
}
if (this.PromiseState === "rejected") {
// 这边也是一样
try {
let result = onRejected(this.PromiseResult);
if (result instanceof MyPromise) {
result.then(
(v) => {
resolve(v);
},
(r) => {
reject(r);
}
);
} else {
resolve(result);
}
} catch (e) {
reject(e);
}
}
if (this.PromiseState === "pending") {
this.callbacks.push({
onResolved,
onRejected,
});
}
});
};
异步任务修改then方法的返回值
前面我们搭建了同步任务的then方法的返回,但是这样的话是否适用于异步任务呢?我们来试试。
js
let p = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve("ok");
}, 1000);
});
const res = p.then((value) => {
console.log(value);
});
console.log(res);
预想应该返回状态为fulfilled
,结果为1的实例对象,但是这里返回的是状态为pending
,结果为undefined
,是因为这里走了then
方法的this.PromiseState === "pending"
判断。
js
if (this.PromiseState === "pending") {
this.callbacks.push({
onResolved,
onRejected,
});
}
这个判断里面的操作知识单纯调用了onResolved
函数和onRejected
函数,没有做类似同步任务搭建的那些操作,所以我们需要给加上,跟同步一样,现在也是只考虑return
的情况和抛出异常的情况。
这里的操作的主要是this.callbacks.push()
的onResolved
函数和onRejected
函数
搭建return的情况
对onResolved
函数和onRejected
函数进行和同步任务一样的修改。
js
MyPromise.prototype.then = function (onResolved, onRejected) {
return new MyPromise((resolve, reject) => {
if (this.PromiseState === "fulfilled") {
try {
let result = onResolved(this.PromiseResult);
if (result instanceof MyPromise) {
result.then(
(v) => {
resolve(v);
},
(r) => {
reject(r);
}
);
} else {
resolve(result);
}
} catch (e) {
reject(e);
}
}
if (this.PromiseState === "rejected") {
try {
let result = onRejected(this.PromiseResult);
if (result instanceof MyPromise) {
result.then(
(v) => {
resolve(v);
},
(r) => {
reject(r);
}
);
} else {
resolve(result);
}
} catch (e) {
reject(e);
}
}
if (this.PromiseState === "pending") {
this.callbacks.push({
onResolved: function () {
// 在这里的操作和同步处理差不多,唯一的区别是这里是对象调用onResolved,所以不能用 this
let result = onResolved(self.PromiseResult);
// 然后进行判断
if (result instanceof Promise) {
result.then(
(v) => {
resolve(v);
},
(r) => {
reject(r);
}
);
} else {
resolve(result);
}
},
onRejected: function () {
// 俺也一样
let result = onRejected(self.PromiseResult);
if (result instanceof Promise) {
result.then(
(v) => {
resolve(v);
},
(r) => {
reject(r);
}
);
} else {
resolve(result);
}
},
});
}
});
};
搭建抛出异常的情况
这里的搭建和前面处理异常的情况一样,也是用try...catch
去处理,位置跟处理同步的时候放的一样。
js
MyPromise.prototype.then = function (onResolved, onRejected) {
return new MyPromise((resolve, reject) => {
if (this.PromiseState === "fulfilled") {
try {
let result = onResolved(this.PromiseResult);
if (result instanceof MyPromise) {
result.then(
(v) => {
resolve(v);
},
(r) => {
reject(r);
}
);
} else {
resolve(result);
}
} catch (e) {
reject(e);
}
}
if (this.PromiseState === "rejected") {
try {
let result = onRejected(this.PromiseResult);
if (result instanceof MyPromise) {
result.then(
(v) => {
resolve(v);
},
(r) => {
reject(r);
}
);
} else {
resolve(result);
}
} catch (e) {
reject(e);
}
}
if (this.PromiseState === "pending") {
this.callbacks.push({
onResolved: function () {
// 跟同步一样,在改变状态执行后续操作的时候包裹
try {
let result = onResolved(self.PromiseResult);
if (result instanceof MyPromise) {
result.then(
(v) => {
resolve(v);
},
(r) => {
reject(r);
}
);
} else {
resolve(result);
}
} catch (e) {
reject(e);
}
},
onRejected: function () {
// 俺也一样
try {
let result = onRejected(self.PromiseResult);
if (result instanceof MyPromise) {
result.then(
(v) => {
resolve(v);
},
(r) => {
reject(r);
}
);
} else {
resolve(result);
}
} catch (e) {
reject(e);
}
},
});
}
});
};
then方法的完善
我们在前面讲搭建then
方法的返回值的时候,会有一段代码反复用到,就是下面这段代码:
js
try {
let result = onResolved(this.PromiseResult);
if (result instanceof MyPromise) {
result.then(
(v) => {
resolve(v);
},
(r) => {
reject(r);
}
);
} else {
resolve(result);
}
} catch (e) {
reject(e);
}
所以我们可以让他复用起来,创建一个callback
函数存放复用代码:
js
MyPromise.prototype.then = function (onResolved, onRejected) {
// 老规矩,因为callback的this获取不到结果和状态,所以提前讲this赋值给self
const self = this;
return new MyPromise((resolve, reject) => {
// type存放的是调用onResolved函数还是onRejected函数
function callback(type) {
try {
let result = type(self.PromiseResult);
if (result instanceof MyPromise) {
result.then(
(v) => {
resolve(v);
},
(r) => {
reject(r);
}
);
} else {
resolve(result);
}
} catch (e) {
reject(e);
}
}
if (this.PromiseState === "fulfilled") {
callback(onResolved);
}
if (this.PromiseState === "rejected") {
callback(onRejected);
}
if (this.PromiseState === "pending") {
this.callbacks.push({
onResolved: function () {
callback(onResolved);
},
onRejected: function () {
callback(onRejected);
},
});
}
});
};
到此,then
方法基本搭建完成。
搭建catch方法及异常穿透
搭建catch方法
搭建完then
方法后现在要开始搭建catch
方法了,不过我们已经搭建完then
方法了,而catch
方法其实就是调用then
方法的第二个回调函数,所以我们可以直接取调用then
方法:
js
MyPromise.prototype.catch = function (onRejected) {
// this就是调用catch方法的Promise实例对象
return this.then(undefined, onRejected);
};
我们来试试能不能实现功能:
js
let p = new MyPromise((resolve, reject) => {
setTimeout(() => {
reject(123);
}, 1000);
});
let result = p.catch((err) => {
console.log(err);
return 1;
});
console.log(result);
可以看到基本功能比如catch
方法的调用和返回都是可以实现的。
搭建异常穿透
catch
的基本功能实现以后现在就要搭建它的异常穿透
功能了,还记得什么是异常穿透
吧。Promise在链式调用的时候如果某一步状态失败了会去直接调用最后的catch
方法。
js
p.then((res) => {})
.then((res) => {})
.then((res) => {})
.catch((err) => {});
在讲搭建异常穿透
之前我们先来看一下几个案例:
js
let p = new MyPromise((resolve, reject) => {
resolve(123);
});
p.then((res) => {
console.log(res);
}).catch((err) => {
console.log(err);
});
js
let p = new MyPromise((resolve, reject) => {
reject(123);
});
p.then((res) => {
console.log(res);
}).catch((err) => {
console.log(err);
});
为什么第一个成功,而第二个会打印报错呢,那是因为第一个是成功状态的Promise实例对象,调用then
方法的时候会走到下面这一步,然后我们也可以在p.then((res) => {})
中获取到这个onResolved
函数。
js
if (this.PromiseState === "fulfilled") {
callback(onResolved);
}
而第二个是失败状态的Promise实例对象,调用then
方法的时候会走到下面这一步,但是我们无法在p.then((res) => {})
中获取到这个onRejected
函数。
js
if (this.PromiseState === "rejected") {
callback(onRejected);
}
那么在调用callback
函数的时候,传入的type
就是undefined
,然后走到let result = type(self.PromiseResult)
就会有异常,因为undefined
不能作为函数,所以会走catch (e) {reject(e)}
去调用reject(e)
,此时打印e
会发现是type is not a function
,所以此时p.then((res) => {})
就会返回一个状态为失败,结果为type is not a function
的实例对象,然后再调用catch
方法,就会打印type is not a function
这个错误。
js
function callback(type) {
try {
let result = type(self.PromiseResult);
if (result instanceof MyPromise) {
result.then(
(v) => {
resolve(v);
},
(r) => {
reject(r);
}
);
} else {
resolve(result);
}
} catch (e) {
reject(e);
}
}
这里比较绕,不知道能不能理解,我在每一步做一个打印看一下,能理解就理解,理解不了也无所谓。
js
MyPromise.prototype.then = function (onResolved, onRejected) {
const self = this;
return new MyPromise((resolve, reject) => {
function callback(type) {
console.log(2,'调用callback方法');
try {
console.log(3,'进入到callback方法的try...catch里');
console.log(type,'传入的onRejected函数');
let result = type(self.PromiseResult);
console.log(result,'获取onRejected函数的结果,走到这一步是p.catch的,因为p.then前面type(self.PromiseResult)会出现异常直接去下面了走不到这一步,此时为undefined是因为p.catch没有有返回值');
if (result instanceof MyPromise) {
result.then(
(v) => {
resolve(v);
},
(r) => {
reject(r);
}
);
} else {
resolve(result);
}
} catch (e) {
console.log(e,`进入try...catch异常以后调用reject函数,此时走完p.then()然后返回一个失败状态结果为${e}的实例对象,然后再调用p.catch方法`);
reject(e);
}
}
if (this.PromiseState === "fulfilled") {
callback(onResolved);
}
if (this.PromiseState === "rejected") {
console.log(1, '走到失败状态的判断,第一次走是因为p.then,第二次走是因为p.catch');
callback(onRejected);
}
if (this.PromiseState === "pending") {
this.callbacks.push({
onResolved: function () {
callback(onResolved);
},
onRejected: function () {
callback(onRejected);
},
});
}
});
};
MyPromise.prototype.catch = function (onRejected) {
// this就是调用catch方法的Promise实例对象
return this.then(undefined, onRejected);
};
let p = new MyPromise((resolve, reject) => {
reject(123);
});
p.then((res) => {
console.log(res);
}).catch((err) => {
console.log(err,'这里是p.catch的打印');
});
所以我们要先对then
方法进行改造,对是否有onRejected
函数进行判断,如果不是函数(也不考虑是否未别的类型,不是函数一般情况下就是没传),就给它设置成函数。
js
MyPromise.prototype.then = function (onResolved, onRejected) {
const self = this;
// 为了实现then方法可以只带一个参数函数,判断onRejected有没有传
if (typeof onRejected !== "function") {
// 如果不是函数,就要给onRejected一个初始值,而不是为 undefined
onRejected = (reason) => {
throw reason;
};
}
return new MyPromise((resolve, reject) => {
function callback(type) {
try {
let result = type(self.PromiseResult);
if (result instanceof MyPromise) {
result.then(
(v) => {
resolve(v);
},
(r) => {
reject(r);
}
);
} else {
resolve(result);
}
} catch (e) {
reject(e);
}
}
if (this.PromiseState === "fulfilled") {
callback(onResolved);
}
if (this.PromiseState === "rejected") {
callback(onRejected);
}
if (this.PromiseState === "pending") {
this.callbacks.push({
onResolved: function () {
callback(onResolved);
},
onRejected: function () {
callback(onRejected);
},
});
}
});
};
然后此时就可以实现功能了,具体分析情况可以看一下我上面怎么分析报错的步骤,然后自己想一想。
js
let p = new MyPromise((resolve, reject) => {
reject(123);
});
p.then((res) => {
console.log(res);
}).catch((err) => {
console.log(err);
});
此时then
方法不用传第二个函数参数的功能就实现了,那么我们是否可以让then
方法连第一个函数参数都不传呢,正常的Promise是可以实现的,所以我们也要搭建一下。
也是一样的操作,判断onResolved
函数是否存在,不存在就给个初始值。
js
MyPromise.prototype.then = function (onResolved, onRejected) {
const self = this;
if (typeof onRejected !== "function") {
onRejected = (reason) => {
throw reason;
};
}
// 让then方法可以不传第一个函数参数
if (typeof onResolved !== "function") {
onResolved = (value) => value;
}
return new MyPromise((resolve, reject) => {
function callback(type) {
try {
let result = type(self.PromiseResult);
if (result instanceof MyPromise) {
result.then(
(v) => {
resolve(v);
},
(r) => {
reject(r);
}
);
} else {
resolve(result);
}
} catch (e) {
reject(e);
}
}
if (this.PromiseState === "fulfilled") {
callback(onResolved);
}
if (this.PromiseState === "rejected") {
callback(onRejected);
}
if (this.PromiseState === "pending") {
this.callbacks.push({
onResolved: function () {
callback(onResolved);
},
onRejected: function () {
callback(onRejected);
},
});
}
});
};
p.then().then((res) => {
console.log(123);
});
好了,现在异常穿透的功能也就正式搭建完了,还记得我们前面搭建then
方法的返回值的时候说的,then
方法返回有三种情况,第一个是没有相关后续处理,后边的任务和前面保持一致;第二个是有相关后续处理但是还未执行;第三个是有相关后续处理且已经执行。当时我们说只考虑第三种情况,现在第一种情况也在这里完成了,而第二种情况不就是前面在讲搭建then
方法的返回的时候其中的异步任务的情况吗,其实也已经完成了。
现在Promise的搭建就基本上完成了。
搭建Promise.resolve()
我们讲resolve
方法的时候,有以下几种情况:
Promise.resolve()
返回一个成功/失败的Promise对象。
接收一个参数,分为四种情况。
- 如果传入一个非Promise类型对象,则返回为一个Promise实例对象,状态为成功,结果值就是传入的参数值;
- 如果传入的是一个Promise类型的对象,会直接返回这个Promise类型的对象。
- 如果传入的是一个thenable对象,thenable对象就是一个带有then方法的对象,resolve会将这个对象直接转化为Promise对象,然后执行thenable对象的then方法。
- 如果不传入参数,会返回一个状态为成功的Promise实例对象,结果为undefined。
这个跟then
方法的返回值搭建差不多,我就直接放代码了,如果不懂,再回去看看then
方法的返回值搭建。
js
MyPromise.resolve = function (value) {
// 返回一个 Promise 对象
return new MyPromise((resolve, reject) => {
// 判断传入的值
if (value instanceof MyPromise) {
// 是 Promise,调用 then()
value.then(
(v) => {
resolve(v);
},
(r) => {
reject(r);
}
);
} else {
// 不是 Promise
// 状态设置为成功,结果就是传入的值,直接调用 resolve()
resolve(value);
}
});
};
试验一下:
js
const p = MyPromise.resolve("ok");
const p2 = MyPromise.resolve(
new MyPromise((resolve, reject) => {
resolve("success");
})
);
const p3 = MyPromise.resolve(
new MyPromise((resolve, reject) => {
reject("no ok");
})
);
const p4 = MyPromise.resolve(
new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve("success");
}, 1000);
})
);
const p5 = MyPromise.resolve(
new MyPromise((resolve, reject) => {
new MyPromise((resolve, reject) => {
setTimeout(() => {
reject("no ok");
}, 1000);
});
})
);
console.log(p, "p");
console.log(p2, "p2");
console.log(p3, "p3");
console.log(p4, "p4");
console.log(p5, "p5"); // 里面的是一个失败状态的Promise对象,但是外面没有调用resolve或者reject,所以状态为pending
搭建Promise.reject()方法
我们讲reject
方法的时候,返回的是一个失败状态,结果值就是传进去值的Promise实例对象。
Promise.reject()
- 无论参数是什么,都会返回一个失败的Promise对象,结果值就是参数本身。
这个就更简单了,直接调用reject
函数就好。
js
MyPromise.reject = function (value) {
// 返回一个 Promise 对象
return new Promise((resolve, reject) => {
reject(value);
});
};