要能手写Promise首先要对其使用方法了熟于心,才能反推其原理,下面我们来一步一步的根据使用方法反推
1.创建promise时的使用方法
new Promise() // 说明promsie就是个类
new Promise(()=>{}) //要传个回调函数进去,我们管这个函数叫执行器,这个执行器会立即执行,也就是当new Promise的时候,此函数会被立即调用
new Promise((resolve, reject)=>{ // 这个函数有两个参数,成功执行函数和失败执行函数,
if(...){
resolve('成功的值')
}else{
reject('失败的原因')
}
})
// 所以它有三个状态,等待(pending),成功(fulfilled),失败(rejected)。
// 状态只能从 pending=>fulfilled 或 pending=>rejected, 一旦状态确定就不可更改,也不可逆。
//调用resolve()方法了,就说明pending=>fulfilled了
//调用reject()方法了,就说明pending=>rejected了
基于上述,写出
class MyPromise { // 它是类
constructor(executor) {
executor(this.resolve, this.reject); // 传进来的函数立即执行且有俩参数
}
status = 'pending'; // 状态
resolve = ()=>{ // 剪头函数,指向promise,而不是window
if(this.status !== 'pending') return; // 状态不可逆
this.status = 'fulfilled'; // 更改状态
}
reject = ()=>{
if(this.status !== 'pending') return;
this.status = 'reject';
}
}
2.promise.then的基本使用方法
let promise = new Promise((resolve, reject)=>{
resolve('成功')
reject('失败')
})
promise.then((value)=>{}, (reason)=>{})
//promise有then方法,有俩参数,第一个是处理成功resolve的,第二个是处理失败reject的
//所以then里面有判断,该调哪个函数
//这两个回调函数都会接收到成功/失败的值
基于上述,加入
class MyPromise {
constructor(executor) {
executor(this.resolve, this.reject);
}
status = 'pending'
value = undefined // 成功之后的值
reason = undefined // 失败之后的原因,初识都应为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);
}
}
}
3.在promise类中加入异步逻辑
// 如果在创建myPromise时加入定时器
let myPro = new MyPromise((resolve, reject)=>{
setTimeout(()=>{
resolve('成功')
}, 2000)
})
myPro.then((v)=>{console.log('@@@')}) // 并没有打印 @@@
// 因为遇到异步setTimeout,setTimeout将从调用栈弹出,交给webAPIs执行,然后继续往下走(详细见js异步事件循环,),
// 遇到myPro.then,执行then时,由于没有执行resolve方法,状态还是pending,then这里缺失了对这种状态的处理
// 所以需要把成功回调函数先储存起来,等待定时器结束,再调用
所以加入处理异步的逻辑
class MyPromise {
constructor(executor) {
executor(this.resolve, this.reject);
}
status = 'pending'
value = undefined
reason = undefined
successCallback = undefined // 成功回调函数
failCallback = undefined // 失败回调函数
resolve = (value)=>{
if(this.status !== 'pending') return;
this.status = 'fulfilled';
this.value = value;
// 如何判断异步执行完了,该调用Callback了呢,
// 就是判断Callback是否存在,因为then里是pending状态的话Callback被存储起来了
if(this.successCallback) {
this.successCallback(this.value)
}
}
reject = (reason)=>{
if(this.status !== 'pending') return;
this.status = 'rejected';
this.reason = reason;
// 如何判断异步执行完了,该调用Callback了呢,
// 就是判断Callback是否存在,因为then里是pending状态的话Callback被存储起来了
if(this.failCallback) {
this.failCallback(this.reason)
}
}
then (successCallback, failCallback) {
if(this.status === 'fulfilled') {
successCallback(this.value);
} else if (this.status === 'rejected') {
failCallback(this.reason);
} else { // 这里说明状态为pending
this.successCallback = successCallback // 将成功回调函数储存起来
this.failCallback = failCallback // 将失败回调函数储存起来
}
}
}
4.实现then方法多次调用,添加多个处理函数
let myPro = new MyPromise((resolve, reject)=>{
resolve('成功')
})
myPro.then((v)=>{console.log('111' + v)}) //=>111成功
myPro.then((v)=>{console.log('222' + v)}) //=>222成功
myPro.then((v)=>{console.log('333' + v)}) //=>333成功
// 看起来是OK的,但是如果加入异步呢
let myPro = new MyPromise((resolve, reject)=>{
setTimeout(()=>{
resolve('成功')
}, 2000)
})
myPro.then((v)=>{console.log('111' + v)})
myPro.then((v)=>{console.log('222' + v)})
myPro.then((v)=>{console.log('333' + v)})// 结果是只会输出一次333成功
// 因为在执行第三次myPro.then时,this.successCallback会被替换成console.log('333' + v),定时器结束时执行resolve方法,只会执行一次successCallback,所以输出一次 333成功
// 所以要将每一次的callback都存储起来,resolve中再依次执行
所以对callback的存储及调用稍作修改
class MyPromise {
constructor(executor) {
executor(this.resolve, this.reject);
}
status = 'pending'
value = undefined
reason = undefined
successCallback = [] // 所以修改成成功回调函数数组
failCallback = [] // 所以修改成失败回调函数数组
resolve = (value)=>{
if(this.status !== 'pending') return;
this.status = 'fulfilled';
this.value = value;
// 修改成循环执行successCallback数组
while(this.successCallback.length) { // =0时停止
this.successCallback.shift()(this.value); //shift是从数组前删除一项,并返回被删除的那一项
}
}
reject = (reason)=>{
if(this.status !== 'pending') return;
this.status = 'rejected';
this.reason = reason;
// 修改成循环执行failCallback数组
while(this.failCallback.length) { // =0时停止
this.failCallback.shift()(this.reason); //shift是从数组前删除一项,并返回被删除的那一项
}
}
then (successCallback, failCallback) {
if(this.status === 'fulfilled') {
successCallback(this.value);
} else if (this.status === 'rejected') {
failCallback(this.reason);
} else { // 这里说明状态为pending
this.successCallback.push(successCallback) // 修改成将每一个成功回调函数储存起来
this.failCallback.push(failCallback) // 修改成将每一个失败回调函数储存起来
}
}
}
5.实现then方法的链式调用
// promise的链式调用方法
let myPro = new MyPromise((resolve, reject)=>{
resolve('成功')
})
myPro.then((v)=>{
console.log(v)
return 100
}).then((v)=>{
console.log(v)
return 200
}).then((v)=>{
console.log(v)
})
// 期望输出 成功 100 200
// 所以每次then返回的是一个新的promise,才能形成链式
// 上一个then中回调函数的返回值,会被下一个then所接收
所以改造then方法
then (successCallback, failCallback) {
let x // 存储成功回调函数的返回值
if(this.status === 'fulfilled') {
x = successCallback(this.value); // 存储成功回调函数的返回值
} else if (this.status === 'rejected') {
x = failCallback(this.reason);
} else {
this.successCallback.push(successCallback)
this.failCallback.push(failCallback)
}
// then方法要返回promise,所以return new MyPromise(()=>{})
return new MyPromise((resolve, reject)=>{
// 要将上一次successCallback的返回值传递给下一个successCallback使用,所以这里调用resolve(x)
resolve(x)
})
}
OK,上面说的是链式调用返回值的使用方式,链式调用还有可以返回promise的使用方式
let p = new Promise((resolve, reject) => {
resolve("执行1")
})
p.then((data) => {
console.log(data) //执行1
return new Promise((resolve, reject) => {resolve("执行2")})
}).then((data) => {
console.log(data) //执行2
return new Promise((resolve, reject) => {reject("执行3")})
}).then((data) => {
console.log('success'+data) // 无输出
}, (data) => {
console.log('fail'+data) //fail执行3
})
// 这种使用方式可以看出,我们需要
// - 在得到x返回值的时候判断其是否为promise
// - 如果是普通值,直接调用resolve(x)
// - 如果是promise对象,查看promise对象的返回结果,决定调用resolve还是reject
so, 继续修改then方法
then (successCallback, failCallback) {
let x
if(this.status === 'fulfilled') {
x = successCallback(this.value);
} else if (this.status === 'rejected') {
x = failCallback(this.reason);
} else {
this.successCallback.push(successCallback)
this.failCallback.push(failCallback)
}
return new MyPromise((resolve, reject)=>{
if(x instanceof MyPromise) { // promise
// 调用x的then方法,看执行的是第一个函数还是第二个函数判断其成功还是失败,再执行我们这个新promise的resolve还是reject
x.then((value)=>{resolve(value)}, (reason)=>{reject(reason)})
} else { // 普通值
resolve(x)
}
})
}
6.实现异步中then方法链式调用
let myPro = new MyPromise((resolve, reject)=>{
setTimeout(()=>{
resolve('成功')
}, 2000)
});
myPro
.then((v)=>{
console.log('1'+v);
return '成功2'
})
.then((v)=>{
console.log('2'+v)
})
// 输出 2undefined 2秒后输出 1成功
// 因为在pending状态的那条分支中,successCallback并没有执行,是被暂时存了起来,所以我们要改造一下
// 变化较大,不过思路很清晰
then (successCallback, failCallback) {
let x;
if(this.status === 'fulfilled') {
x = successCallback(this.value);
return new MyPromise((resolve, reject)=>{
if(x instanceof MyPromise) {
x.then((value)=>{resolve(value)}, (reason)=>{reject(reason)})
} else {
resolve(x)
}
})
} else if (this.status === 'rejected') {
x = failCallback(this.reason);
return new MyPromise((resolve, reject)=>{
if(x instanceof MyPromise) {
x.then((value)=>{resolve(value)}, (reason)=>{reject(reason)})
} else {
resolve(x)
}
})
} else {
// 2.不能像以前一样直接在最后return MyPromise ...resolve(x),你必须要需要拿到结果x后,再调用resolve(x),所以return MyPromise的代码位置要做下调整
return new MyPromise((resolve, reject)=>{
// 1.首先这里不在直接push callback,给callback外再套一层函数,目的是能在这里拿到执行结果x
this.successCallback.push(()=>{
x = successCallback(this.value);
if(x instanceof MyPromise) {
x.then((value)=>{resolve(value)}, (reason)=>{reject(reason)})
} else {
resolve(x)
}
});
this.failCallback.push(()=>{
x = failCallback(this.reason);
if(x instanceof MyPromise) {
x.then((value)=>{resolve(value)}, (reason)=>{reject(reason)})
} else {
resolve(x)
}
});
})
}
}
上面代码then方法很臃肿,冗余较多,可提取如下
class MyPromise {
constructor(executor) {
executor(this.resolve, this.reject);
}
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()(); // 3.无需传值,直接调用即可
}
}
reject = (reason)=>{
if(this.status !== 'pending') return;
this.status = 'rejected';
this.reason = reason;
while(this.failCallback.length) {
this.failCallback.shift()();// 3.无需传值,直接调用即可
}
}
then (successCallback, failCallback) {
let x;
// 2. return MyPromise提至最外面一层来,下面这些都是立即执行的,所以在promise前面,还是里面都是一样的
let promise2 = new MyPromise((resolve, reject)=>{
if(this.status === 'fulfilled') {
x = successCallback(this.value);
this.resolvePromise(x, resolve, reject) // 直接调用resolvePromise
} else if (this.status === 'rejected') {
x = failCallback(this.reason);
this.resolvePromise(x, resolve, reject)
} else {
this.successCallback.push(()=>{
x = successCallback(this.value);
this.resolvePromise(x, resolve, reject)
});
this.failCallback.push(()=>{
x = failCallback(this.reason);
this.resolvePromise(x, resolve, reject)
});
}
})
return promise2;
}
// 1.这里提取一个函数,专门做x值判断及后续处理
resolvePromise(x, resolve, reject) {
if(x instanceof MyPromise) {
x.then((value)=>{resolve(value)}, (reason)=>{reject(reason)})
} else {
resolve(x)
}
}
}
7.将 then 方法的参数变为可选参数
// then 方法可以不接收任何参数,而继续向后传值
let p = new Promise((resolve, reject) => {
resolve("成功")
})
p.then().then((v)=>console.log(v)); // => 成功
let p = new Promise((resolve, reject) => {
reject("失败")
})
p.then().then(()=>{}, (reason)=>console.log(reason)); // => 失败
解决方法:
p.then().then((v)=>console.log(v));
// 没有参数要是想成功,相当于手动加个(value) => {return value} 当成功入参 ,(reason) => {throw reason}当失败入参
p
.then(value => value, (reason) => {throw reason})
.then((v)=>console.log(v), (reason)=>console.log(reason));
// then前面加入判断及手动赋值
then (successCallback, failCallback) {
successCallback = successCallback ? successCallback : (v) => v;
failCallback = failCallback ? failCallback : (reason) => {throw reason};
let x;
let promise2 = new MyPromise((resolve, reject)=>{
// ... 都没变
})
return promise2;
}
8.捕获错误
- 到目前为止,我们始终没有对代码的健壮性进行补充,其实就是加try catch,那么加在哪里呢,加在使用者传进去的各种回调函数中,他们执行失败,就转为直接调reject
class MyPromise {
constructor(executor) {
try { // 1.首先是初始化时,我们传入的执行器函数
executor(this.resolve, this.reject);
} catch (e) {
this.reject(e)
}
}
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()();
}
}
reject = (reason)=>{
if(this.status !== 'pending') return;
this.status = 'rejected';
this.reason = reason;
while(this.failCallback.length) {
this.failCallback.shift()();
}
}
then (successCallback, failCallback) {
successCallback = successCallback ? successCallback : (v) => v;
failCallback = failCallback ? failCallback : (reason) => {throw reason};
let x;
let promise2 = new MyPromise((resolve, reject)=>{
if(this.status === 'fulfilled') {
try { // 2.then中我们传入的成功回调要加
x = successCallback(this.value);
this.resolvePromise(x, resolve, reject)
} catch (e) {
this.reject(e)
}
} else if (this.status === 'rejected') {
try { // 2.then中我们传入的失败回调要加,下面也是一样
x = failCallback(this.reason);
this.resolvePromise(x, resolve, reject)
} catch (e) {
this.reject(e)
}
} else {
this.successCallback.push(()=>{
try {
x = successCallback(this.value);
this.resolvePromise(x, resolve, reject)
} catch (e) {
this.reject(e)
}
});
this.failCallback.push(()=>{
try {
x = failCallback(this.reason);
this.resolvePromise(x, resolve, reject)
} catch (e) {
this.reject(e)
}
});
}
})
return promise2;
}
resolvePromise(x, resolve, reject) {
if(x instanceof MyPromise) {
x.then((value)=>{resolve(value)}, (reason)=>{reject(reason)})
} else {
resolve(x)
}
}
}
到这里,promise的核心代码已经结束了,(二)中将实现 all,finally,catch等方法