你应该掌握的JavaScript高阶技能(七)

7. 手写 Promise 学习笔记

朋友们,大家好!本期内容是《你应该掌握的 JavaScript 高阶技能》的第期,主要内容:Promise 手写实现,如果您还没有学习过 Promise ,可以先看此文章 Promise 与 异步编程 学习笔记,最后,望本文能对您有所帮助!☀️

本期内容参考:
尚硅谷 Web 前端 Promise 教程从入门到精通
特别鸣谢❤️

7.1 初始结构搭建

//声明构造函数 
function Promise(executor/*执行器函数*/) {
  
}
// Promise.then()
// 根据 Promise 对象状态执行相应的回调函数
// 添加 then 方法
Promise.prototype.then = function() {
    // then 方法接收两个参数
    // 一个成功的回调函数 一个失败的回调函数
}

7.2 resolve 与 reject 结构搭建

//声明构造函数 
function Promise(executor) {
      // resolve 函数
      function resolve(data) { }
      // reject 函数
      function reject(data) { }
      // 执行器函数 且是同步调用
      executor(resolve,reject) { }
}
// then 方法
Promise.prototype.then = function(onResolved/*成功回调*/,onRejected/*失败回调*/) {

}

7.3 resolve 与 reject 代码实现

// 调用 resolve 和 reject 会更改 Promise 对象状态
//声明构造函数 
function Promise(executor) {
  // 添加相应的属性
  this.PromiseState = "pending";
  this.PromiseResult = null;
  // resolve 函数
  function resolve(data) {
	// console.log(this); // window
    // 修改对象的状态 pending --> fulfilled
    self.PromiseState = "fulfilled";
    // 设置对象结果值
    self.PromiseResult = data;
  }
  // reject 函数
  function reject(data) {
    // 实现方法跟 resolve 类似
    // 修改对象的状态 pending --> rejected
    self.PromiseState = "rejected";
    self.PromiseResult = data; 
  }
  // 执行器函数 且是同步调用
  executor(resolve,reject) { }
}

在这里插入图片描述

在这里插入图片描述

7.4 throw 抛出异常改变状态

  • 如果 executor 执行器中抛出异常,此时我们的 Promise 对象状态不会发生改变。
try {
  executor(resolve,reject);
} catch(e) {
  // 修改 promise 对象状态设置为失败
  reject(e);
}

7.5 Promise 对象只能修改一次

// todo...
function resolve(data) {
    // 判断状态是否已经改变
    if(self.PromiseState !== 'pending') {
        return;
    }
    // todo...
}
// reject 函数同理!
// todo...

7.6 then 方法执行回调

// then 方法
Promise.prototype.then = function (onResolved,onRejected) {
  if(this.PromiseState === 'fulfilled') {
    //调用成功的回调函数
    onResolved(this.PromiseResult);
  }
  if(this.PromiseState === 'rejected') {
    //调用失败的回调函数
    onRejected(this.PromiseResult);
  }
}

7.7 异步任务回调的执行

let p = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('ok');
    }, 1000);
});
p.then((value) => {
    console.log(value);
},(reason) => {
    console.log(reason);
})
  • 此时我们写的 Promise,执行后是没有任何输出。

  • 思路:当执行器函数 executor 内部是一个异步任务的时候,则先指定的回调函数 p.then(成功回调,失败回调)(我们先保存),等到执行器函数执行完毕也就是改变状态之后再执行指定的回调函数

function Promise(executor) {
    // todo...
    this.callbacks = [];
    // 异步任务所保存的回调函数数组
    // todo...
}
  • 保存成功的回调
// 状态未改变 --> 就保存回调函数
Promise.prototype.then = function(onResolved,onRejected) {
    // todo...
    if(this.PromiseState === 'pending') {
        // 表明异步任务
        // 保存相应的回调函数
        this.callbacks = {
            onResolved,
            onRejected,
        }
    }
    // todo...
}

  • 状态改变,执行相应的回调函数
function Promise(executor) {
    // todo...
    function resolve(data) {
       	if(self.PromiseState !== 'pending') {
        	return;
    	}
    	self.PromiseState = "fulfilled";
    	self.PromiseResult = data;
        // 状态变为 fulfilled 我们要执行成功的回调函数
        if(self.callbacks.onResolved) {
            self.callbacks.onResolved(data);
        }
    }
    function reject(data) {
        if(self.PromiseState !== 'pending') {
        	return;
    	}
    	self.PromiseState = "rejected";
    	self.PromiseResult = data;
        // 状态变为 reject 我们要执行失败的回调函数
        if(self.callbacks.onRejected) {
            self.callbacks.onRejected(data);
        }
  	}
    // todo...
}
  • 这样在 1 秒后就可以在控制台输出 “ok”。

7.8 指定多个回调的实现

  • 如果我们指定多个回调函数,此时只会执行我们最后一次指定的回调函数,也就是说前面指定的回调函数都被覆盖了。
// 第一个调用 then 方法
result.then((value) => {
    console.log(value);
}, (reason) => {
    console.log(reason);
});
// 第二次调用 then 方法
result.then((value) => {
    alert(value);
}, (reason) => {
    alert(reason);
});
// 结果只有弹窗内容,控制台没有输出
  • 覆盖的原因:此前保存回调函数的方式:直接赋值。
// todo...
this.callbacks = {
    onResolved,
    onRejected,
}
// todo...
  • 改善:将回调函数加入数组中
Promise.prototype.then = function (onResolved,onRejected) {
    // todo...
    if(this.PromiseState === 'pending') {
        this.callbacks.push({
            onResolved,
            onRejected,
        })
    }
    // todo...
}
  • 此时 callbacks 数组中每一个数组元素都是一个包含成功与失败的回调函数的对象
  • Promise 对象状态改变为 fulfilled 时,将同步调用 callbacks 中成功的回调函数。rejected 同理!
function Promise(executor) {
    // todo...
    const self = this;
    function resolve(data) {
        // todo...
        // 调用成功的回调函数
        /*
        原:
        if(self.callbacks.onResolved) {
            self.callbacks.onResolved(data);
        }
        */
        self.callbacks.forEach((item) => {
           item.onResolved(data); 
        });   
    }
    function reject(data) {
        // todo...
        // 调用失败的回调函数
        self.callbacks.forEach((item) => {
           item.onRejected(data);
        }); 
    }
}

7.9 同步修改状态 then 方法结果返回

  • 回顾:then 方法返回结果是一个 Promise 对象,其状态取决于指定的回调函数内部执行的结果
Promise.prototype.then = function(onResolved, onRejected) {
    // 1. 返回 Promise 对象
    return new Promise((resolve,reject) => {
        // todo...
    });
}
Promise.prototype.then = function(onResolved, onRejected) {
    // 1. 返回 Promise 对象
    return new Promise((resolve,reject) => {
        if(this.PromiseState === 'fulfilled') {
            // 执行成功的回调
            try {
                 let result = onResolved(this.PromiseResult);
                // 3. 判断result 返回值是否是 Promise 对象
                if(result instanceof Promise) {
                    // 此 promise 的结果就会成为 then 方法返回的结果
                    // 调用 then 方法为其指定成功与失败的回调函数
                    result.then(v => {
                        // 内部 fulfilled 返回 fulfilled
                        resolve(v);
                    },r => {
                       // 内部 rejected 返回 rejected
                       reject(r);
                    });
                    
                } else {
                    // 4. 结果的对象状态为成功
                    resolve(result);
                }
            } catch(e) {
                // 2. 抛出异常
                // 结果的对象状态为失败
                reject(e);
            }
		}
        // todo...
    });
}

7.10 异步任务修改 then 方法

  • 此时如果执行器函数内部是一个异步任务, Promise 对象调用 then 方法返回的结果是 pending
  • 这是因为调用 then方法时,此时的状态为 pending,仅是保存了相应的回调函数,没有对 then 返回结果进行处理。
  • 所以这里不仅仅要保存相应的回调函数,也要对 then 返回结果进行处理!
Promise.prototype.then = function (onResolved, onRejected) {
    const self = this;
    // todo...
    return new Promise((resolve,reject) => {
        if(this.PromiseState === 'pending') {
        	this.callbacks.push({
            	// 成功的回调函数并改变 Promise.then() 方法返回结果对象的状态
            	onResolved: function() {
                	try {
                        let result = onResolved(self.PromiseResult);
                        // 判断 result 是否为 Promise 对象
                        //处理返回结果
                        if(result instanceof Promise) {
                            result.then(v => {
                        // 内部 fulfilled 返回 fulfilled
                                resolve(v);
                            },r => {
                       // 内部 rejected 返回 rejected
                                reject(r);
                            });
                        } else {
                            resolve(result);
                        }
                	} catch(e) {
                    	reject(e);
                	}
            	},
            	// 成功的回调函数 
            	onRejected: function() {
                    // onRejected 是跟 onResolved 一样的!只是 result = onRejected(this)
            	}
        	})
    	}
    })
}

7.11 then 方法完善与优化

  • 我们发现 onResolvedonRejected 回调函数内容基本一致,优化代码,使其封装函数 callback
Promise.prototype.then = function (onResolved, onRejected) {
    const self = this;
    function callback(type/*action*/) {
        try {
            // 对应 let result = onResolved(self.PromiseResult);
        	// let result = onRejected(self.PromiseResult);
            let result = type(self.PromiseResult);
            if (result instanceof Promise) {
                //如果是 promise 对象
                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 基本完成!

7.12 catch 方法

  • catch 方法本质上就是 then 的语法糖,指定失败的回调函数并执行。
Promise.prototype.catch = function(onRejected) {
    return this.then(undefined,onRejected);
}
  • 处理链式调用 then 问题
  • 异常穿透和值传递
let p = new Promise((resolve,reject) => {
	setTimeout(() => {
       resolve('ok');
    });
});
p.then(value => {
    return new Promise((resolve, reject) => {
        resolve('success');
    });
}).then((value) => {
    console.log(value); // success
}).then(value => {
    console.log(value); // undefined
});
// 此时,我们为 then 方法只传递了成功的回调函数,失败的回调函数 onRejected 为 undefined,会抛出 TypeError: type in not a function at callback
// 因为我们没有指定失败回调,对于这样情况,我们要默认传入参数
Promise.prototype.then = function (onResolved, onRejected) {
	// 判断回调函数参数
    if(typeof onRejected !== 'function') {
        onRejected = (reason) => {
            throw reason;
        }
    }
    if(typeof onResolved !== 'function') {
		onResolved = value => value;
    }
}

7.13 resolve 方法封装

// Promise.resolve 类方法
Promise.resolve = function(value) {
    return new Promise((resolve,reject) => {
		if(value instanceof Promise) {
            value.then(v => {
                resolve(v);
            }, r => {
                reject(r);
            })
        } else {
            resolve(value);
        }
    })
}

7.14 reject 方法封装

Promise.reject = function (reason) {
    return new Promise((undefined, reject) => {
        reject(reason);
  	});
}

7.15 all 方法封装

Promise.all = function(promises) {
    return new Promise((resolve,reject) => {
        let count = 0;
        // 成功的 PromiseResult
        let arr = []
        // 遍历 promises 数组
		for(let i = 0; i < promises.length; i++) {
            promises[i].then(v => {
                // 每个 promise 对象都成功
                // 将当前 promise 对象存入数组
                // 使用 arr.push(v) 有问题,要考虑异步任务问题!
                count++;
                arr[i] = v;
                if(count === promises.length) {
                    resolve(arr);
                }
            }, r => {
                reject(r);
			})
	}
    
    })
}

7.16 race 方法封装

  • 返回一个新的 promise, 第一个完成的 promise 的结果状态就是最终的结果状态
Promise.race = function(promises) {
    return new Promise((resolve,reject) => {
        for (let i = 0; i < promises.length; i++) {
            promises[i].then(v => {
                resolve(v);
            }, r => {
                reject(r);
            })
        }
	});
}

7.17 then 方法回调的异步执行

let p = new Promise((resolve,reject) => {
    resolve('ok');
    console.log('1');
})

p.then(value => {
    console.log(value);
    console.log('3');
});

console.log('2');

// 输出结果为 1 2 ok 3
// 说明 then 方法是异步任务 内部是同步任务
  • 简单方法:使用 setTimeout 包装,但是这样是有问题的,这里仅仅是模拟。
function Promise(executor) {
    // todo...
	function resolve(data) {
		setTimeout(() => {
            self.callbacks.forEach(item => {
                item.onResolved(data);
            })
        });
	}
    function reject(data) {
		setTimeout(() => {
            self.callbacks.forEach(item => {
                item.onRejected(data);
            })
        });
	}
    // todo...
}

Promise.prototype.then = function (onResolved, onRejected) {
    // todo...
    if (this.PromiseState === 'fulfilled') {
    // 异步执行
    	setTimeout(() => {
        	callback(onResolved);
    	})
	}
	if (this.PromiseState === 'rejected') {
    	setTimeout(() => {
        	callback(onRejected);
    	})
	}
    // todo...
}

7.18 class 版本

//class 类
class Promise {
    //构造方法
    constructor(executor) {
        //添加属性
        this.PromiseState = 'pending';
        this.PromiseResult = null;
        this.callbacks = [];
        //保存实例对象的 this 值
        const self = this;

        //resolve 函数
        function resolve(data) {
            if (self.PromiseState !== 'pending') {
                return;
            }
            //console.log(this);//window
            //1.修改对象改变状态(promiseState)
            self.PromiseState = 'fulfilled';//resolved
            //2.设置对象结果值(promiseResult)
            self.PromiseResult = data;
            //调用成功的回调函数
            setTimeout(() => {
                self.callbacks.forEach(item => {
                    item.onResolved(data);
                })
            })
        }
        //reject 函数
        function reject(data) {
            //判断状态
            if (self.PromiseState !== 'pending') {
                return;
            }
            //1.修改对象改变状态(promiseState)
            self.PromiseState = 'rejected';//rejected
            //2.设置对象结果值(promiseResult)
            self.PromiseResult = data;
            //调用失败的回调函数
            setTimeout(() => {
                self.callbacks.forEach(item => {
                    item.onRejected(data);
                })
            })
        }

        // executor 执行器函数是同步调用的
        try {
            executor(resolve, reject);
        } catch (e) {
            //修改 promise 对象状态设置为【失败】
            reject(e);
        }
    }

    //then 方法
    then(onResolved, onRejected) {
        const self = this;
        //判断回调函数参数
        if (typeof onRejected !== 'function') {
            onRejected = reason => {
                throw reason;
            }
        }
        if (typeof onResolved !== 'function') {
            onResolved = value => value;
        }

        return new Promise((resolve, reject) => {
            //封装函数
            function callback(type) {
                try {
                    //获取回调函数的执行结果
                    //console.log(this.PromiseResult);
                    //console.log(onResolved);
                    let result = type(self.PromiseResult);
                    //console.log(result);
                    if (result instanceof Promise) {
                        //如果是 promise 对象
                        result.then(v => {
                            resolve(v);
                        }, r => {
                            reject(r);
                        }
                        )

                    } else {
                        //结果的对象状态为成功
                        resolve(result);
                    }
                } catch (e) {
                    reject(e);
                }
            }
            //调用回调函数 PromiseState
            // this 指向调用者 即创建的实例
            if (this.PromiseState === 'fulfilled') {
                //异步执行
                setTimeout(() => {
                    callback(onResolved);
                })

            }
            if (this.PromiseState === 'rejected') {
                setTimeout(() => {
                    callback(onRejected);
                })
            }
            //判断 pending 状态
            if (this.PromiseState === 'pending') {
                //保存回调函数
                this.callbacks.push({
                    onResolved: function () {
                        callback(onResolved);
                    },
                    onRejected: function () {
                        callback(onRejected);
                    }
                })
            }
        })
    }

    //catch 方法
    catch(onRejected) {
        return this.then(undefined, onRejected);
    }

    //resolve 方法
    //类方法 静态方法 不属于实例对象的方法
    static resolve(value) {
        return new Promise((resolve, reject) => {
            if (value instanceof Promise) {
                value.then(v => {
                    resolve(v)
                }, r => {
                    reject(r);
                })
            } else {
                resolve(value);
            }
        })
    }

    //reject 方法
    static reject(reason) {
        return new Promise((undefined, reject) => {
            reject(reason);
        })
    }

    //all 方法
    static all(promises) {
        return new Promise((resolve, reject) => {
            //遍历
            let count = 0;
            let arr = [];
            for (let i = 0; i < promises.length; i++) {
                promises[i].then(v => {
                    //每个 promise 对象都成功
                    count++;
                    //将当前 promise 对象存入数组
                    // arr.push(v);//顺序有问题
                    arr[i] = v;
                    if (count === promises.length) {
                        resolve(arr);
                    }
                }, r => {
                    reject(r);
                })
            }
        })
    }

    //race 方法
    static race(promises) {
        return new Promise((resolve, reject) => {
            for (let i = 0; i < promises.length; i++) {
                promises[i].then(v => {
                    resolve(v);
                }, r => {
                    reject(r);
                })
            }
        })
    }
}

第七期学习内容就这么多啦,如果您觉得内容不错的话,望您能关注🤞点赞👍收藏❤️一键三连!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值