2023年都过去一半了,你还不会手写Promise?
手写Promise不是为了应付面试,是为了锻炼自己的编程思维,理解Promise核心。在工作中能用好Promise。从而也是对自己职业生涯的一种交代。如果一味的觉得没什么意义,那这篇文章就不用往下看了!
首先写任何API或者框架不要追求一步到位,需要慢慢的分析,一步一个脚印的来写。最后完善。
看到这里废话不多说。直接开整!
class myPromise{
constructor(executor) {
//这个玩意对应的就是外面传进来的函数,
executor(this.resolve.bind(this),this.reject.bind(this))
}
resolve(data){ //成功的回调
console.log(data)
}
reject(res){ //失败的回调
console.log(res)
}
}
new myPromise(function (resolve, reject) {
resolve('成功!')
//reject('失败!')
})
写到这里咱就直接测试一下吧! 去浏览器打印,发现2个回调都能正常调用!非常棒
但是Promise的状态只能是一个要么成功,要么失败,要么等待,并且是不可逆的,一但状态变了,就已经固定了!所以得加一个状态管理。同时在成功与失败的回调里面加上判断。
class myPromise{
#state = 'pending' //初始化状态 pending
constructor(executor) {
//这个玩意对应的就是外面传进来的函数,
executor(this.resolve.bind(this),this.reject.bind(this))
}
resolve(data){ //成功的回调
if(this.#state !== 'pending') return
this.#state = 'fulfilled' //改变状态fulfilled
console.log(data)
}
reject(res){ //失败的回调
if(this.#state !== 'pending') return
this.#state = 'rejected' //改变状态rejected
console.log(res)
}
}
new myPromise(function (resolve, reject) {
resolve('成功!')
//reject('失败!')
})
以上一个简单的Promise就写好了。 根据不同的状态来控制成功还是失败。
但是状态控制好了之后,我们还需要拿到结果呀。 用的Promise的小伙伴都知道,结果需要在then方法的回调函数里面拿。接下来我们就写 then方法吧
class myPromise{
#state = 'pending' //初始化状态 pending
#result = undefined //存放成功或者失败的结果
constructor(executor) {
//这个玩意对应的就是外面传进来的函数,
executor(this.resolve.bind(this),this.reject.bind(this))
}
resolve(data){ //成功的回调
if(this.#state !== 'pending') return
this.#state = 'fulfilled' //改变状态fulfilled
this.#result = data //赋值成功的结果
}
reject(res){ //失败的回调
if(this.#state !== 'pending') return
this.#state = 'rejected' //改变状态rejected
this.#result = res //赋值失败的结果
}
then(onFulfilled, onRejected){
if(this.#state == 'fulfilled'){
onFulfilled(this.#result) //如果状态是成功的,就把结果给then方法的第一个回调
}else if(this.#state == 'rejected'){
onRejected(this.#result) //如果状态是失败的,就把结果给then方法的第二个回调
}
}
}
new myPromise(function (resolve, reject) {
resolve('成功!')
//reject('失败!')
}).then((success)=>{
console.log(success)
},(error)=>{
console.log(error)
})
以上Promise的then方法也写好了。 一个完整的Promise就实现了呀。 怎么样简单吗? 伙伴们!
但是在测试的时候我们会发现一个问题。我们都知道Promise的出现,是为了优化异步代码。 那如果我们用定时器模拟一个1秒才返回成功的异步请求呢? 此时还能打印出成功吗?
new myPromise(function (resolve, reject) {
setTimeout(() => {
resolve('成功')
}, 1000)
}).then((success) => {
console.log(success)
}, (error) => {
console.log(error)
})
此刻异步就失效了,什么也没打印。那要怎么样才能解决这个问题呢。 很简单,我们只需要在resolve与reject这2个方法改变状态的时候调用一下onFulfilled或者onRejected
但是细心的小伙伴会想,onFulfilled与onRejected 是then方法的2个回调,只能在then这个方法里面调, resolve与reject这2个改变状态的方法,没办法直接调用呀。这个时候我们可以在then方法先收集onFulfilled与onRejected,然后保存到一个变量里面!
class myPromise{
#state = 'pending' //初始化状态 pending
#result = undefined //存放成功或者失败的结果
#stack = null //定义一个存放onFulfilled, onRejected的变量
constructor(executor) {
//这个玩意对应的就是外面传进来的函数,
executor(this.resolve.bind(this),this.reject.bind(this))
}
resolve(data){ //成功的回调
if(this.#state !== 'pending') return
this.#state = 'fulfilled' //改变状态fulfilled
this.#result = data //赋值成功的结果
this.#stack.onFulfilled(this.#result) //状态改变的时候调用
}
reject(res){ //失败的回调
if(this.#state !== 'pending') return
this.#state = 'rejected' //改变状态rejected
this.#result = res //赋值失败的结果
this.#stack.onRejected(this.#result) //状态改变的时候调用
}
then(onFulfilled, onRejected){
if (this.#state == 'fulfilled') {
onFulfilled(this.#result) //如果状态是成功的,就把结果给then方法的第一个回调
} else if (this.#state == 'rejected') {
onRejected(this.#result) //如果状态是失败的,就把结果给then方法的第二个回调
} else {
this.#stack = { //如果状态是pending等待的,就把结果先收集好。等状态改变再去调用
onFulfilled, onRejected
}
}
}
}
new myPromise(function (resolve, reject) {
setTimeout(() => {
resolve('成功')
}, 1000)
}).then((success) => {
console.log(success)
}, (error) => {
console.log(error)
})
现在是不是异步的问题就解决啦。但是又引出一个问题。如果我多次调用then方法呢? 始终只会打印出一个"成功"
let data = new myPromise(function (resolve, reject) {
setTimeout(() => {
resolve('成功')
}, 1000)
})
data.then((success) => {
console.log(success)
}, (error) => {
console.log(error)
})
data.then((success) => {
console.log(success)
}, (error) => {
console.log(error)
})
这个问题也很好解决,我们只需要把#stack这个变量变成一个数组用来存放就搞定了!
class myPromise{
#state = 'pending' //初始化状态 pending
#result = undefined //存放成功或者失败的结果
#stack = [] //定义一个存放onFulfilled, onRejected的变量
constructor(executor) {
//这个玩意对应的就是外面传进来的函数,
executor(this.resolve.bind(this),this.reject.bind(this))
}
resolve(data){ //成功的回调
if(this.#state !== 'pending') return
this.#state = 'fulfilled' //改变状态fulfilled
this.#result = data //赋值成功的结果
this.#stack.forEach(item => { //循环数组
item.onFulfilled(this.#result)
})
}
reject(res){ //失败的回调
if(this.#state !== 'pending') return
this.#state = 'rejected' //改变状态rejected
this.#result = res //赋值失败的结果
this.#stack.forEach(item => { //循环数组
item.onRejected(this.#result)
})
}
then(onFulfilled, onRejected){
if (this.#state == 'fulfilled') {
onFulfilled(this.#result) //如果状态是成功的,就把结果给then方法的第一个回调
} else if (this.#state == 'rejected') {
onRejected(this.#result) //如果状态是失败的,就把结果给then方法的第二个回调
} else {
this.#stack.push({ //如果状态是pending等待的,就把结果先收集好。等状态改变再去调用
onFulfilled, onRejected
})
}
}
}
讲到这里我们忽略了一个关键性的问题。then方法是一个可链式调用的。我们这种写法能链式调用吗?
let data = new myPromise(function (resolve, reject) {
setTimeout(() => {
resolve('成功')
}, 1000)
})
data.then((success) => {
console.log(success)
}).then((success) => {
console.log(success)
})
很显然,这样会报错! 所以我们要让then方法返回一个Promise。接下来再改造一下
class myPromise{
#state = 'pending' //初始化状态 pending
#result = undefined //存放成功或者失败的结果
#stack = [] //定义一个存放onFulfilled, onRejected的变量
constructor(executor) {
//这个玩意对应的就是外面传进来的函数,
executor(this.resolve.bind(this),this.reject.bind(this))
}
resolve(data){ //成功的回调
if(this.#state !== 'pending') return
this.#state = 'fulfilled' //改变状态fulfilled
this.#result = data //赋值成功的结果
this.#stack.forEach(item => { //状态改变的时候调用
item.onFulfilled(this.#result)
})
}
reject(res){ //失败的回调
if(this.#state !== 'pending') return
this.#state = 'rejected' //改变状态rejected
this.#result = res //赋值失败的结果
this.#stack.forEach(item => { //状态改变的时候调用
item.onRejected(this.#result)
})
}
then(onFulfilled, onRejected){
return new myPromise((resolve, reject)=>{
if (this.#state == 'fulfilled') {
onFulfilled(this.#result) //如果状态是成功的,就把结果给then方法的第一个回调
} else if (this.#state == 'rejected') {
onRejected(this.#result) //如果状态是失败的,就把结果给then方法的第二个回调
} else {
this.#stack.push({ //如果状态是pending等待的,就把结果先收集好。等状态改变再去调用
onFulfilled, onRejected
})
}
})
}
}
此时就不报错了,也返回了一个Promise。 但是Promise还有一个特点,就是当第一个then不是一个函数的时候,返回值会穿透给下一个then。 这句话很绕口,也不好理解。 直接看代码吧
let data = new myPromise(function (resolve, reject) {
setTimeout(() => { resolve('成功') }, 1000)
})
data.then(null).then((success) => { //第一个then的回调是一个null
console.log(success)
})
那么怎么解决这个问题呢? 我们只需要判断一下then的回调方法是不是一个函数,如果不是函数就把它的参数传入Promise的resolve与reject做处理
class myPromise {
#state = 'pending' //初始化状态 pending
#result = undefined //存放成功或者失败的结果
#stack = [] //定义一个存放onFulfilled, onRejected的变量
constructor(executor) {
//这个玩意对应的就是外面传进来的函数,
executor(this.resolve.bind(this), this.reject.bind(this))
}
resolve(data) { //成功的回调
if (this.#state !== 'pending') return
this.#state = 'fulfilled' //改变状态fulfilled
this.#result = data //赋值成功的结果
this.#stack.forEach(item => { //状态改变的时候调用
if (typeof item.onFulfilled == 'function') { //判断是否是一个函数
item.onFulfilled(this.#result)
} else {
item.resolve(this.#result)
}
})
}
reject(res) { //失败的回调
if (this.#state !== 'pending') return
this.#state = 'rejected' //改变状态rejected
this.#result = res //赋值失败的结果
this.#stack.forEach(item => { //状态改变的时候调用
if (typeof item.onRejected == 'function') { //判断是否是一个函数
item.onRejected(this.#result)
} else {
item.reject(this.#result)
}
})
}
then(onFulfilled, onRejected) {
return new myPromise((resolve, reject) => {
if (this.#state == 'fulfilled') {
if (typeof onFulfilled == 'function') { //判断是否是一个函数
onFulfilled(this.#result) //如果状态是成功的,就把结果给then方法的第一个回调
} else {
resolve(this.#result)
}
} else if (this.#state == 'rejected') {
if (typeof onRejected == 'function') { //判断是否是一个函数
onRejected(this.#result) //如果状态是成功的,就把结果给then方法的第一个回调
} else {
reject(this.#result)
}
} else {
this.#stack.push({ //如果状态是pending等待的,就把结果先收集好。等状态改变再去调用
onFulfilled, onRejected, resolve, reject
})
}
})
}
}
这个问题解决后,还引出一个相似的问题。就是then方法里面return的值,可以被下一个then方法的回调接收到
let data = new myPromise(function (resolve, reject) {
setTimeout(() => { resolve('成功') }, 1000)
})
data.then(()=>{
return 111
}).then((success) => {
console.log(success) //111
})
针对这个问题,我们只需要把onFulfilled与onRejected执行后的返回值给resolve与reject就行了
class myPromise {
#state = 'pending' //初始化状态 pending
#result = undefined //存放成功或者失败的结果
#stack = [] //定义一个存放onFulfilled, onRejected的变量
constructor(executor) {
//这个玩意对应的就是外面传进来的函数,
executor(this.resolve.bind(this), this.reject.bind(this))
}
resolve(data) { //成功的回调
if (this.#state !== 'pending') return
this.#state = 'fulfilled' //改变状态fulfilled
this.#result = data //赋值成功的结果
this.#stack.forEach(item => { //状态改变的时候调用
if (typeof item.onFulfilled == 'function') {
const data = item.onFulfilled(this.#result)
try {
item.resolve(data) //返回值作为参数 ,传给resolve
} catch (err) {
item.reject(err)
}
} else {
item.resolve(this.#result)
}
})
}
reject(res) { //失败的回调
if (this.#state !== 'pending') return
this.#state = 'rejected' //改变状态rejected
this.#result = res //赋值失败的结果
this.#stack.forEach(item => { //状态改变的时候调用
if (typeof item.onRejected == 'function') {
const data = item.onRejected(this.#result)
try {
item.resolve(data) //返回值作为参数 ,传给resolve
} catch (err) {
item.reject(err)
}
} else {
item.reject(this.#result)
}
})
}
then(onFulfilled, onRejected) {
return new myPromise((resolve, reject) => {
if (this.#state == 'fulfilled') {
if (typeof onFulfilled == 'function') {
const data = onFulfilled(this.#result) //如果状态是成功的,就把结果给then方法的第一个回调
try {
resolve(data) //返回值作为参数 ,传给resolve
} catch (err) {
reject(err)
}
} else {
resolve(this.#result)
}
} else if (this.#state == 'rejected') {
if (typeof onRejected == 'function') {
const data = onRejected(this.#result) //如果状态是成功的,就把结果给then方法的第一个回调
try {
resolve(data) //返回值作为参数 ,传给resolve
} catch (err) {
reject(err)
}
} else {
reject(data)
}
} else {
this.#stack.push({ //如果状态是pending等待的,就把结果先收集好。等状态改变再去调用
onFulfilled, onRejected, resolve, reject
})
}
})
}
}
那如果就是then方法里面return的值又是一个promise呢?可以被下一个then接收吗?
let data = new myPromise(function (resolve, reject) {
setTimeout(() => { resolve('成功') }, 1000)
})
data.then(()=>{
return new myPromise(function (resolve, reject) {
setTimeout(() => { resolve('成功') }, 1000)
})
}).then((success) => {
console.log(success) //成功
})
这里还需要对细节做一下改造。
function isPromise(val) { //判断是否是一个promise
if (val !== null && (typeof val == 'object' || typeof val == 'function')) {
return typeof val.then === 'function'
}
}
class myPromise {
#state = 'pending' //初始化状态 pending
#result = undefined //存放成功或者失败的结果
#stack = [] //定义一个存放onFulfilled, onRejected的变量
constructor(executor) {
//这个玩意对应的就是外面传进来的函数,
executor(this.resolve.bind(this), this.reject.bind(this))
}
resolve(data) { //成功的回调
if (this.#state !== 'pending') return
this.#state = 'fulfilled' //改变状态fulfilled
this.#result = data //赋值成功的结果
this.#stack.forEach(item => { //状态改变的时候调用
if (typeof item.onFulfilled == 'function') {
const data = item.onFulfilled(this.#result)
try {
if (isPromise(data)) { //如果是promise 就调用 then
data.then(item.resolve, item.reject)
} else {
item.resolve(data)
}
} catch (err) {
item.reject(err)
}
} else {
item.resolve(this.#result)
}
})
}
reject(res) { //失败的回调
if (this.#state !== 'pending') return
this.#state = 'rejected' //改变状态rejected
this.#result = res //赋值失败的结果
this.#stack.forEach(item => { //状态改变的时候调用
if (typeof item.onRejected == 'function') {
const data = item.onRejected(this.#result)
try {
if (isPromise(data)) {
data.then(item.resolve, item.reject) //如果是promise 就调用 then
} else {
item.resolve(data)
}
} catch (err) {
item.reject(err)
}
} else {
item.reject(this.#result)
}
})
}
then(onFulfilled, onRejected) {
return new myPromise((resolve, reject) => {
if (this.#state == 'fulfilled') {
if (typeof onFulfilled == 'function') {
const data = onFulfilled(this.#result) //如果状态是成功的,就把结果给then方法的第一个回调
try {
if (isPromise(data)) {
data.then(resolve, reject) //如果是promise 就调用 then
} else {
resolve(data)
}
} catch (err) {
reject(err)
}
} else {
resolve(this.#result)
}
} else if (this.#state == 'rejected') {
if (typeof onRejected == 'function') {
const data = onRejected(this.#result) //如果状态是成功的,就把结果给then方法的第一个回调
try {
if (isPromise(data)) {
data.then(resolve, reject)//如果是promise 就调用 then
} else {
resolve(data)
}
} catch (err) {
reject(err)
}
} else {
reject(data)
}
} else {
this.#stack.push({ //如果状态是pending等待的,就把结果先收集好。等状态改变再去调用
onFulfilled, onRejected, resolve, reject
})
}
})
}
}
这样我们手写的promise 就全部写完了! 我们只需要要简单的测试一下,结合官方的promise 组合我们自己写的都能成功返回。
let data = new myPromise((res, rej) => {
setTimeout(() => { res('成功') }, 1000)
}).then(res => {
return new Promise((res, rej) => {
setTimeout(() => { res('成功') }, 1000)
})
})
async function getData() {
let res = await data
console.log(res) //成功
}
getData()
由于上面重复代码太多,咱们再优化一波
function isPromise(val) {
if (val !== null && (typeof val == 'object' || typeof val == 'function')) {
return typeof val.then === 'function'
}
}
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class myPromise {
#state = 'pending' //初始化状态 pending
#result = undefined //存放成功或者失败的结果
#stack = [] //定义一个存放onFulfilled, onRejected的变量
constructor(executor) {
executor(this.resolve.bind(this), this.reject.bind(this))
}
setState(state, result) {
this.#state = state //改变状态fulfilled
this.#result = result //赋值成功的结果
this.walk()
}
setData(fn, resolve, reject) {
if (typeof fn == 'function') {
try {
const data = fn(this.#result)
if (isPromise(data)) {
data.then(resolve, reject)
} else {
resolve(data)
}
} catch (err) {
reject(err)
}
} else {
resolve(this.#result)
}
}
walk() {
if (this.#state == 'pending') return
while (this.#stack.length) {
const { onFulfilled, onRejected, resolve, reject } = this.#stack.shift()
if (this.#state == FULFILLED) {
this.setData(onFulfilled, resolve, reject)
} else if (this.#state == REJECTED) {
this.setData(onRejected, resolve, reject)
}
}
}
resolve(data) { //成功的回调
if (this.#state !== 'pending') return
this.setState('fulfilled', data)
}
reject(res) { //失败的回调
if (this.#state !== 'pending') return
this.setState('rejected', res)
}
then(onFulfilled, onRejected) {
return new myPromise((resolve, reject) => {
this.#stack.push({ //收集
onFulfilled, onRejected, resolve, reject
})
this.walk()
})
}
}
现在手写Promise就全部完工了。。小伙伴们学废了吗?
转载自:https://juejin.cn/post/7241838768016097340