发布于 2024 年 10 月 21 日,星期一
Promise原理学习总结
Promise是一种用于处理异步操作的对象,它代表了某个异步操作的最终完成或失败,并返回其结果。博客内容可能包括Promise的基本概念、创建和使用Promise的方法、Promise链式调用、错误处理机制以及Promise在实际开发中的应用场景。通过学习Promise的原理,开发者可以更好地理解和掌握异步编程,提高代码的可读性和可维护性。
手写 Promise 原理,最通俗易懂版!!!
前言
大家好,这篇文章是我这段时间看到把Promise 以最通俗的话 讲的很清楚的一篇文章,我也是重新学习了Promise原理,文章内容大多借鉴原创。
let p1 = new Promise((resolve, reject) => {
resolve('成功');
reject('失败');
});
console.log(p1);
let p2 = new Promise((resolve, reject) => {
reject('失败');
resolve('成功');
});
console.log(p2);
let p3 = new Promise((resolve, reject) => {
throw('报错');
});
console.log(p3);
p1: Promise {<fulfilled>: '成功'}
[[Prototype]]: Promise
[[PromiseState]]: "fulfilled"
[[PromiseResult]]: "成功"
p2: Promise {<rejected>: "失败"}
[[Prototype]]: Promise
[[PromiseState]]: "rejected"
[[PromiseResult]]: "失败"
p3: Promise {<rejected>: "报错"}
[[Prototype]]: Promise
[[PromiseState]]: "rejected"
[[PromiseResult]]: "报错"
四个知识点:
1、执行resolve,Promise 状态会变成fulfilled;
2、执行reject,Promise 状态会变成rejected;
3、Promise 只以第一次为准,第一次成功就永久为fulfilled,第一次失败就永远的状态为rejected;
4、Promise 中有throw的话,相当于执行了reject;
1、实现 resolve 和 reject
注意:Promise 的初始状态是 pending 状态
1、这里很重要的一步是resolve和reject要绑定this,为什么要绑定this呢?
- 这是为了 resolve 和 reject 的
this指向永远指向当前的MyPromise实例(自定义实例),防止随着函数的执行环境的改变而改变;
class MyPromise {
constructor(executor) {
// 初始值
this.initValue();
// 初始化this指向
this.initBind();
// 执行传过来的函数
executor(this.resolve, this.reject);
}
initValue() {
// 初始化值
this.PromiseResult = null; // 终值
this.PromiseState = 'pending'; // 状态
}
initBind() {
// 初始化this
this.resolve = this.resolve.bind(this);
this.reject = this.reject.bind(this);
}
resolve(value) {
// 如果执行resolve,状态变成fulfilled
this.PromiseState = 'fulfilled';
// 终值为传进来的值
this.PromiseResult = value;
}
reject(reason) {
// 如果不执行reject,状态变为rejected
this.PromieState = 'rejected';
// 终值为传进来的reason
this.PromiseResult = reason;
}
}
测试 MyPromise
cosnt test1 = new MyPromise((resovle,reject) => {
resolve('成功');
})
console.log(test1);
// MyPromise {PromiseState: 'fulfilled', PromiseResult: '成功'}
cosnt test2 = new MyPromise((resovle,reject) => {
resolve('失败');
})
console.log(test2);
// MyPromise {PromiseState: 'rejected', PromiseResult: '失败'}
2、状态不可变
上面的代码是有问题的,上面问题呢?看下面的代码:
cosnt test1 = new MyPromise((resovle,reject) => {
resolve('成功');
reject('失败');
})
console.log(test1);
// MyPromise {PromiseState: 'rejected', PromiseResult: '失败'}
- 正确的状态是
fulfilled,结果是成功,这里明显没有以第一次为准。 - 上面说了,Promise 只以
第一次为准,第一次成功就为fulfilled,第一次失败就永远状态为rejected,具体的流程呢?下面是关于这个流程的图:- Promise 有三种状态
pending:等待中,也就是初始状态fulfilled:成功状态rejected:失败状态
- Promise 有三种状态
- 这时状态一旦从
pending变为fulfilled或者rejected,那么次 Promise 实例的状态就定死了。
解决问题也会容易,加个判断条件就可以:
resolve(value) {
// state是不可变的
+ if (this.PromiseState !== 'pending') return;
// 如果执行resolve,状态变为fulfilled
this.PromiseState = 'fulfilled';
// 终值为传进来的值
this.PromiseResult = value;
}
reject(reason) {
// state是不可变的
+ if (this.PromiseState !== 'pending') return;
// 如果执行reject,状态变为rejected
this.PromiseState = 'rejected';
// 终值为传进来的reason
this.PromiseResult = reason;
}
再次测试 MyPromise:
const test1 = new MyPromise((resolve, reject) => {
// 只以第一次为准
resolve('成功');
reject('失败');
});
console.log(test1);
// MyPromise { PromiseState: 'fulfilled', PromiseResult: '成功' }
3、throw
Promise 中有
throw的话,就相当于执行了reject。这就要使用try catch了。
+ try {
// 执行传进来的函数
executor(this.resolve, this.reject)
+ } catch (e) {
// 捕捉到错误直接执行reject
+ this.reject(e)
+ }
看看效果:
const test3 = new MyPromise((resolve, reject) => {
throw '失败';
});
console.log(test3);
// MyPromise { PromiseState: 'rejected', PromiseResult: '失败' }
> then
咱们平时使用 then 方法是这么用的:
/上面代码是精简版,不利于我们阅读,大家可以参考精简版下面这段代码/;
// 马上输出 ”成功“
const p1 = new Promise((resolve, reject) => {
resolve('成功');
}).then(
res => console.log(res),
err => console.log(err)
);
const p1 = new Promise((resolve, reject) => {
resolve('成功');
}).then(
res => {
console.log(res);
},
err => {
console.log(err);
}
);
// 1秒后输出 ”失败“
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('失败');
}, 1000);
}).then(
res => console.log(res),
err => console.log(err)
);
const p2 = new Promise((resovle, reject) => {
setTimeout(() => {
reject('失败');
}, 1000);
}).then(
res => {
console.log(res);
},
err => {
console.log(err);
}
);
// 链式调用 输出 200
const p3 = new Promise((resolve, reject) => {
resolve(100);
})
.then(
res => 2 * res,
err => console.log(err)
)
.then(
res => console.log(res),
err => console.log(err)
);
const p3 = new Promise((resolve, reject) => {
resolve(100);
})
.then(
res => {
return 2 * res;
},
err => {
console.log(err);
}
)
.then(
res => {
console.log(res);
},
err => {
console.log(err);
}
);
总结点如下:
then接受两个回调,一个是成功回调,一个是失败回调。- 当 Promise 中的状态为
fulfilled执行成功回调,为rejected执行失败回调。 - 如 resolve 或 reject 在定时器中,
则定时器结束后再执行then。 - then 执行
链式调用,下一次 then 执行受上一次then返回值的影响。
接下来我们一步一步来实现
1、实现 then
then(onFulfilled, onRejected) {
// 接收两个回调 onFulfilled, onRejected
// 参数校验,确保一定是函数
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }
if (this.PromiseState === 'fulfilled') {
// 如果当前为成功状态,执行第一个回调
onFulfilled(this.PromiseResult)
} else if (this.PromiseState === 'rejected') {
// 如果当前为失败状态,执行第二哥回调
onRejected(this.PromiseResult)
}
}
测试一下:
/上面代码是精简版,不利于我们阅读,大家可以参考精简版下面这段代码/;
// 输出 ”成功“
const test = new MyPromise((resolve, reject) => {
resolve('成功');
}).then(
res => console.log(res),
err => console.log(err)
);
const test = new MyPromise((resolve, reject) => {
resolve('成功');
}).then(
res => {
console.log(res);
},
err => {
console.log(err);
}
);
2.定时器情况
上面的方法实现了then的基本功能,那如果是定时器该怎么办呢?
/上面代码是精简版,不利于我们阅读,大家可以参考精简版下面这段代码/;
// 1秒后输出 ”成功“
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('失败');
}, 1000);
}).then(
res => console.log(res),
err => console.log(err)
);
const test = new MyPromise((resolve, reject) => {
setTimeout(() => {
reject('失败');
}, 1000);
}).then(
res => {
console.log(res);
},
err => {
console.log(err);
}
);
我们不能确保 1s 后才执行 then 函数,但是我们可以保证 1s 后再执行 then 里面的回调,可能这里大家有点懵,下面同样用一张图来解释:
也就是在这 1s 的时间内,我们可以先把 then 里面的两个回调保存起来,然后等到 1s 过后,执行 resolve 或者 reject,然后再去判断状态,并且判断要去执行刚刚保存的两个回调中的那一个回调。
此时,问题就来了,我们怎么知道当前 1s 还没走完甚至还没开始走呢?其实很好判断,只有状态是pending,那就证明定时器还没跑完,以为如果定时器跑完的话,那状态肯定就不是pending,而是fulfilled或者rejected。
那还有一个问题,那我们用什么来保存这些回调呢?建议大家使用数组,因为一个 promise 实例可能会多次then,用数组把一个一个都保存起来。
initValue() {
// 初始化值
this.PromiseResult = null // 终值
this.PromiseState = 'pending' // 状态
+ this.onFulfilledCallbacks = [] // 保存成功回调
+ this.onRejectedCallbacks = [] // 保存失败回调
}
resolve(value) {
// state是不可变的
if (this.PromiseState !== 'pending') return
// 如果执行resolve,状态变为fulfilled
this.PromiseState = 'fulfilled'
// 终值为传进来的值
this.PromiseResult = value
// 执行保存的成功回调
+ while (this.onFulfilledCallbacks.length) {
+ this.onFulfilledCallbacks.shift()(this.PromiseResult)
+ }
}
reject(reason) {
// state是不可变的
if (this.PromiseState !== 'pending') return
// 如果执行reject,状态变为rejected
this.PromiseState = 'rejected'
// 终值为传进来的reason
this.PromiseResult = reason
// 执行保存的失败回调
+ while (this.onRejectedCallbacks.length) {
+ this.onRejectedCallbacks.shift()(this.PromiseResult)
+ }
}
then(onFulfilled, onRejected) {
// 接收两个回调 onFulfilled, onRejected
// 参数校验,确保一定是函数
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }
if (this.PromiseState === 'fulfilled') {
// 如果当前为成功状态,执行第一个回调
onFulfilled(this.PromiseResult)
} else if (this.PromiseState === 'rejected') {
// 如果当前为失败状态,执行第二哥回调
onRejected(this.PromiseResult)
+ } else if (this.PromiseState === 'pending') {
+ // 如果状态为待定状态,暂时保存两个回调
+ this.onFulfilledCallbacks.push(onFulfilled.bind(this))
+ this.onRejectedCallbacks.push(onRejected.bind(this))
+ }
}
写完代码,我们来测试一下,看看效果:
const test2 = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('成功'); // 1秒后输出 成功
// resolve('成功') // 1秒后输出 失败
}, 1000);
}).then(
res => console.log(res),
err => console.log(err)
);
const test = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('成功'); // 1秒后输出 成功
// resolve('成功') // 1秒后输出 失败
}, 1000);
}).then(
res => {
console.log(res);
},
err => {
console.log(err);
}
);
3、链式调用
then 执行链式调用 ,下一次 then 执行受上一次then返回值的影响,大家可以看下面这个例子:
// 链式调用 输出 200
const p3 = new Promise((resolve, reject) => {
resolve(100);
})
.then(
res => 2 * res,
err => console.log(err)
)
.then(
res => console.log(res),
err => console.log(err)
);
const p3 = new Promise((resolve, reject) => {
resolve(100);
})
.then(
res => {
return 2 * res;
},
err => {
console.log(err);
}
)
.then(
res => {
console.log(res);
},
err => {
console.log(err);
}
);
// 链式调用 输出300
const p4 = new Promise((resolve, reject) => {
resolve(100);
})
.then(
res => new Promise((resolve, reject) => resolve(3 * res)),
err => console.log(err)
)
.then(
res => console.log(res),
err => console.log(err)
);
const p4 = new Promise((resolve, reject) => {
resolve(100);
})
.then(
res => {
return new Promise((resolve, reject) => {
resolve(3 * res);
});
},
err => {
console.log(err);
}
)
.then(
res => {
console.log(res);
},
err => {
console.log(err);
}
);
从上面的例子中,可以得到下面几个知识点:
- then 方法本身会返回一个新的 Promise 对象。
- 如果返回值是 promise 对象,返回值为成功,新 promise 也就是成功。
- 如果返回值是 promise 对象,返回值为失败,新 promise 也就是失败。
- 如果返回值非 promise 对象,新 promise 对象就是成功,值为此返回值。
大家都知道 then 是 promise 上的方法,那如何实现 then 完还能再次 then 呢?很简单,then 执行后会返回一个promise对象就行了,就能保证 then 完还能继续执行 then:
实现代码:
then(onFulfilled, onRejected) {
// 接收两个回调 onFulfilled, onRejected
// 参数校验,确保一定是函数
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }
var thenPromise = new MyPromise((resolve, reject) => {
const resolvePromise = cb => {
try {
const x = cb(this.PromiseResult)
if (x === thenPromise) {
// 不能返回自身哦
throw new Error('不能返回自身。。。')
}
if (x instanceof MyPromise) {
// 如果返回值是Promise
// 如果返回值是promise对象,返回值为成功,新promise就是成功
// 如果返回值是promise对象,返回值为失败,新promise就是失败
// 谁知道返回的promise是失败成功?只有then知道
x.then(resolve, reject)
} else {
// 非Promise就直接成功
resolve(x)
}
} catch (err) {
// 处理报错
reject(err)
}
}
if (this.PromiseState === 'fulfilled') {
// 如果当前为成功状态,执行第一个回调
resolvePromise(onFulfilled)
} else if (this.PromiseState === 'rejected') {
// 如果当前为失败状态,执行第二个回调
resolvePromise(onRejected)
} else if (this.PromiseState === 'pending') {
// 如果状态为待定状态,暂时保存两个回调
// 如果状态为待定状态,暂时保存两个回调
this.onFulfilledCallbacks.push(resolvePromise.bind(this, onFulfilled))
this.onRejectedCallbacks.push(resolvePromise.bind(this, onRejected))
}
})
// 返回这个包装的Promise
return thenPromise
}
测试一下,看一下效果:
/上面代码是精简版,不利于我们阅读,大家可以参考精简版下面这段代码/;
const test3 = new MyPromise((resolve, reject) => {
resolve(100); // 输出 状态:成功 值: 200
// reject(100) // 输出 状态:失败 值:300
})
.then(
res => 2 * res,
err => 3 * err
)
.then(
res => console.log(res),
err => console.log(err)
);
const test3 = new Promise((resolve, reject) => {
resolve(100);
})
.then(
res => {
new Promise((resolve, reject) => {
reject(2 * res);
});
},
err => {
new Promise((resolve, reject) => {
resolve(2 * res);
});
}
)
.then(
res => {
console.log(111, res);
},
err => {
console.log(222, err);
}
);
const test4 = new MyPromise((resolve, reject) => {
resolve(100); // 输出 状态:失败 值:200
// reject(100) // 输出 状态:成功 值:300
// 这里可没搞反哦。真的搞懂了,就知道了为啥这里是反的
})
.then(
res => new MyPromise((resolve, reject) => reject(2 * res)),
err => new MyPromise((resolve, reject) => resolve(2 * res))
)
.then(
res => console.log(res),
err => console.log(err)
);
const test5 = new Promise((resolve, reject) => {
// resolve(100);
reject(100);
})
.then(
res => {
return 2 * res;
},
err => {
return 3 * err;
}
)
.then(
res => {
console.log(res);
},
err => {
console.log(err);
}
);
4、微任务
看过js执行机制的兄弟都知道,then 方法是微任务,啥叫微任务呢?其实不知道也不要紧,我通过下面例子让你知道:
const p = new Promise((resolve, reject) => {
resolve(1)
}).then(res => console.log(res), err => console.log(err))
console.log(2)
输出顺序是 2 1
const p = new Promise((resolve, reject) => {
resolve(1);
}).then(
res => {
console.log(res);
},
err => {
console.log(err);
}
)
console.log(2);
// 输出值:2 1
为啥不是 1 2 呢?因为 then 是个微任务啊。。。同样,我们也要给我们的 MyPromise 加上这个特性(我这里使用定时器,大家别介意哈).
只需要让resolvePromise函数异步执行就可以了
const resolvePromise = cb => {
setTimeout(() => {
try {
const x = cb(this.PromiseResult);
if (x === thenPromise) {
// 不能返回自身哦
throw new Error('不能返回自身。。。');
}
if (x instanceof MyPromise) {
// 如果返回值是Promise
// 如果返回值是promise对象,返回值为成功,新promise就是成功
// 如果返回值是promise对象,返回值为失败,新promise就是失败
// 谁知道返回的promise是失败成功?只有then知道
x.then(resolve, reject);
} else {
// 非Promise就直接成功
resolve(x);
}
} catch (err) {
// 处理报错
reject(err);
}
});
};
测试一下:
const test4 = new MyPromise((resolve, reject) => {
resolve(1)
}).then(res => console.log(res), err => console.log(err))
console.log(2)
输出顺序 2 1
const p = new Promise((resolve, reject) => {
resolve(1);
}).then(
res => {
console.log(res);
},
err => {
console.log(err);
}
)
console.log(2);
// 输出值:2 1
> Promise 的其他方法
all
- 接收一个 Promise 数组,数组中如有非 Promise 项,则此项当做成功
- 如果所有 Promise 都成功,则返回成功结果数组
- 如果有一个 Promise 失败,则返回这个失败结果
static all(promises) {
const result = []
let count = 0
return new MyPromise((resolve, reject) => {
const addData = (index, value) => {
result[index] = value
count++
if (count === promises.length) resolve(result)
}
promises.forEach((promise, index) => {
if (promise instanceof MyPromise) {
promise.then(res => {
addData(index, res)
}, err => reject(err))
} else {
addData(index, promise)
}
})
})
}
race
- 接收一个 Promise 数组,数组中如有非 Promise 项,则此项当做成功
- 哪个 Promise 最快得到结果,就返回那个结果,无论成功失败
static race(promises) {
return new MyPromise((resolve, reject) => {
promises.forEach(promise => {
if (promise instanceof MyPromise) {
promise.then(res => {
resolve(res)
}, err => {
reject(err)
})
} else {
resolve(promise)
}
})
})
}
allSettled
- 接收一个 Promise 数组,数组中如有非 Promise 项,则此项当做成功
- 把每一个 Promise 的结果,集合成数组,返回
static allSettled(promises) {
return new Promise((resolve, reject) => {
const res = []
let count = 0
const addData = (status, value, i) => {
res[i] = {
status,
value
}
count++
if (count === promises.length) {
resolve(res)
}
}
promises.forEach((promise, i) => {
if (promise instanceof MyPromise) {
promise.then(res => {
addData('fulfilled', res, i)
}, err => {
addData('rejected', err, i)
})
} else {
addData('fulfilled', promise, i)
}
})
})
}
any
any 与 all 相反
- 接收一个 Promise 数组,数组中如有非 Promise 项,则此项当做成功
- 如果有一个 Promise 成功,则返回这个成功结果
- 如果所有 Promise 都失败,则报错
static any(promises) {
return new Promise((resolve, reject) => {
let count = 0
promises.forEach((promise) => {
promise.then(val => {
resolve(val)
}, err => {
count++
if (count === promises.length) {
reject(new AggregateError('All promises were rejected'))
}
})
})
})
}
}
Promise 递归调用
// 错误代码
let count = 0;
const promise2 = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve();
}, 1500);
})
};
const promiseFunction = () => {
return new Promise((resolve, reject) => {
promise2().then(() => {
if (count < 6) {
count += 1;
return promiseFunction();
} else {
resolve(`Count reached 6, resolving promise.`);
}
})
});
};
promiseFunction().then(message => {
console.log(message);
});
// 这样写在 promiseFunction().then 的回调中是一直拿不到 message 信息的。
// 方法一:resolve(promiseFunction()) 则表示你在 resolve 方法里传入 promiseFunction() 的结果
let count = 0;
const promise2 = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve();
}, 1500);
})
};
const promiseFunction = () => {
return new Promise((resolve, reject) => {
promise2().then(() => {
if (count < 6) {
count += 1;
+ resolve(promiseFunction());
} else {
resolve(`Count reached 6, resolving promise.`);
}
})
});
};
promiseFunction().then(message => {
console.log(message);
});
// 方法二:
let count = 0;
const promise2 = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve();
}, 1500);
});
};
const promiseFunction = () => {
return promise2().then(() => {
if (count < 6) {
count += 1;
// 这里需要返回 Promise 对象,否则外部的 then 方法无法接收到内部的 resolve 值。
return promiseFunction();
} else {
return `Count reached 6, resolving promise.`;
}
});
};
promiseFunction().then(message => {
console.log(message);
});
// 方法三:promiseFunction().then(resolve) 表示你先执行 promiseFunction(),然后在它的结果上调用 then 方法。
let count = 0;
const promise2 = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve();
}, 1500);
});
};
const promiseFunction = () => {
return promise2().then(() => {
if (count < 6) {
count += 1;
// 这里需要返回 Promise 对象,否则外部的 then 方法无法接收到内部的 resolve 值。
promiseFunction().then(resolve);
} else {
return `Count reached 6, resolving promise.`;
}
});
};
promiseFunction().then(message => {
console.log(message);
});