JS手写代码篇---手写Promise
4、手写promise
Promise
是一个内置对象,用于处理异步操作。Promise
对象表示一个尚未完成但预期将来会完成的操作。
Promise 的基本结构
一个 Promise
对象通常有以下状态:
- pending(进行中):初始状态,既不是成功也不是失败。
- fulfilled(已成功):操作成功完成。
- rejected(已失败):操作失败。
promise原生代码:
let promise = new Promise((resolve, reject) => {setTimeout(() => {resolve("下次一定");})});// then的用法promise.then((result) => {console.log(result);},(error) => {console.log(error);}).then((result) => {console.log(result);},(error) => {console.log(error);});
(1).初始结构
使用promise的基本结构:
let promise = new Promise((resolve, reject) => {resolve("下次一定");});
先创建一个Promise实例,传入一个函数,函数有两个参数,而且promise是有三种状态的,执行reject还是resolve都要根据promise的状态来定
// 因为promise的创建是:promise = new Promise(() => {})// 所以原生promose我们使用类class Commitment{// prmise有三种状态,全局定义static PENDING = "待定";static FULFILLED = "成功";static REJECTED = "拒绝";// promise一般会传入一个函数且参数为resolve和rejectconstructor(func){// 状态this.status = Commitment.PENDING;// 结果this.result = null;this.error = null;// this调用自身的方法func(this.resolve , this.reject)}// resolveresolve(result){// 判断状态if(this.status === Commitment.PENDING){Commitment.status = Commitment.FULFILLED;this.result = result;}}// rejectreject(error){if(this.status === Commitment.PENDING){Commitment.status = Commitment.REJECTED;this.error = error;}}}
(2).this指向
但是我们测试发现了问题:
promise自己写.html:38 Uncaught TypeError: Cannot read properties of undefined (reading 'status')at reject (promise自己写.html:38:21)at promise自己写.html:49:9at new Commitment (promise自己写.html:24:13)at promise自己写.html:47:21
Cannot read properties of undefined (reading ‘status’):this已经跟丢了
解决class的this指向问题:箭头函数、bind或者proxy
constructor(func){// 状态this.status = Commitment.PENDING;// 结果this.result = null;this.error = null;// this调用自身的方法,确保这些方法在被调用时,this 的值指向当前的 Commitment 实例func(this.resolve.bind(this) , this.reject.bind(this));}
bind(this)
会创建一个新的函数,这个新函数的this
值被永久绑定到当前Commitment
实例(即this
指向当前的Commitment
对象)。- 这样,无论
resolve
或reject
方法在哪里被调用,this
始终指向Commitment
实例,确保你可以正确访问实例的属性和方法(如this.status
、this.result
等)。
(3).then
传入两个参数,一个是成功的回调,一个是失败的回调,但是还要判断条件
promise.then((result) => {console.log(result);},(error) => {console.log(error);})
then(onFULFILLED , onREJECTED){// 判断状态if(this.status === Commitment.FULFILLED){// 执行成功的回调onFULFILLED(this.result);}if(this.status === Commitment.REJECTED){// 执行失败的回调onREJECTED(this.error);}}
(4).执行异常
1、执行报错
// 测试const promise = new Commitment((resolve, reject) => {throw new Error("我报错啦");});
抛出错误,要识别
// promise一般会传入一个函数且参数为resolve和rejectconstructor(func){// 状态this.status = Commitment.PENDING;// 结果this.result = null;this.error = null;// 在执行之前try...catch捕获错误try{// this调用自身的方法,确保这些方法在被调用时,this 的值指向当前的 Commitment 实例func(this.resolve.bind(this) , this.reject.bind(this));}catch(error){// 捕获错误this.reject(error);}}
2、当传入的then参数不为函数的时候
// 测试const promise = new Commitment((resolve, reject) => {resolve("成功了");});promise.then (undefined,//成功error => { console.log("失败:", error.message); } // 失败回调(可选))
我们会发现它报错了,解决方法就是判断类型
promise自己写.html:57 Uncaught TypeError: onFULFILLED is not a functionat Commitment.then (promise自己写.html:57:17)at promise自己写.html:72:13
// then方法:传入两个参数,一个是成功的回调,一个是失败的回调// 但是还要判断条件then(onFULFILLED , onREJECTED){// 判断状态以及是否为函数if(this.status === Commitment.FULFILLED && typeof onFULFILLED === 'function'){// 执行成功的回调onFULFILLED(this.result);}if(this.status === Commitment.REJECTED && typeof onREJECTED === 'function'){// 执行失败的回调onREJECTED(this.error);}}
(5)异步
then实现异步–setTimeOut
// then方法:传入两个参数,一个是成功的回调,一个是失败的回调// 但是还要判断条件then(onFULFILLED , onREJECTED){// 判断状态以及是否为函数if(this.status === Commitment.FULFILLED && typeof onFULFILLED === 'function'){// 执行成功的回调onFULFILLED(this.result);}if(this.status === Commitment.REJECTED && typeof onREJECTED === 'function'){// 执行失败的回调onREJECTED(this.error);}}
resolve是异步的,then也是调用的
原生的promise:
console.log("第一步");let promise = new Promise((resolve, reject) => {console.log("第二步");setTimeout(() => {resolve("下次一定");console.log("第四步");})});// then的用法promise.then((result) => {console.log(result);},(error) => {console.log(error);})console.log("第三步");
第一步
第二步
第三步
第四步
下次一定
但是手写promise
console.log("第一步");const promise = new Commitment((resolve, reject) => {console.log("第二步");setTimeout(() => { //执行thenresolve("成功了");console.log("第四步");}, 0);});promise.then (result => {console.log("成功" , result.message); },//成功error => { console.log("失败:", error.message); } // 失败回调(可选))console.log("第三步");
第一步
第二步
第三步
第四步
原因:then中状态判断的问题,settimeout之后它就执行then方法了,但是此时的状态还是待定,但是then里面并没有处理待定状态
(6)回调保存
解决:判定待定状态,保留then里面的函数,所以我们要用数组保存函数
但是我们发现还是有问题resolve和reject是在事件循环末尾执行的,他们也要添加resolve和reject
<script>// 因为promise的创建是:promise = new Promise(() => {})// 所以原生promose我们使用类class Commitment {// prmise有三种状态,全局定义static PENDING = "待定";static FULFILLED = "成功";static REJECTED = "拒绝";// promise一般会传入一个函数且参数为resolve和rejectconstructor(func) {// 状态this.status = Commitment.PENDING;// 结果this.result = null;this.error = null;// 数组:用于保存成功和失败的回调this.onFulfilledCallbacks = [];//新增this.onRejectedCallbacks = [];//新增// 在执行之前try...catch捕获错误try {// this调用自身的方法,确保这些方法在被调用时,this 的值指向当前的 Commitment 实例func(this.resolve.bind(this), this.reject.bind(this));} catch (error) {// 捕获错误this.reject(error);}}// resolveresolve(result) {setTimeout(() => {// 判断状态if (this.status === Commitment.PENDING) {this.status = Commitment.FULFILLED;this.result = result;// 执行成功的回调--新增this.onFulfilledCallbacks.forEach((callback) => {callback(result);});}});}// rejectreject(error) {setTimeout(() => {if (this.status === Commitment.PENDING) {this.status = Commitment.REJECTED;this.error = error;// 执行失败的回调--新增this.onRejectedCallbacks.forEach((callback) => {callback(error);});}});}// then方法:传入两个参数,一个是成功的回调,一个是失败的回调// 但是还要判断条件then(onFULFILLED, onREJECTED) {// 判断状态以及是否为函数// 如果是待定,就要保存函数到数组里面--新增if (this.status === Commitment.PENDING) {this.onFulfilledCallbacks.push(onFULFILLED);this.onRejectedCallbacks.push(onREJECTED);}if (this.status === Commitment.FULFILLED &&typeof onFULFILLED === "function") {// then是异步的setTimeout(() => {// 执行成功的回调onFULFILLED(this.result);});}if (this.status === Commitment.REJECTED &&typeof onREJECTED === "function") {// then是异步的setTimeout(() => {// 执行失败的回调onREJECTED(this.error);});}}}// 测试console.log("第一步");const promise = new Commitment((resolve, reject) => {console.log("第二步");setTimeout(() => {resolve("成功了");console.log("第四步");}, 0);});promise.then((result) => {console.log(result);}, //成功(error) => {console.log(error);} // 失败回调(可选));console.log("第三步");</script>
(7)链式
promise的链式是新建一个promise对象,我们直接返回this就好了
then(onFULFILLED, onREJECTED) {// 判断状态以及是否为函数// 如果是待定,就要保存函数到数组里面if (this.status === Commitment.PENDING) {this.onFulfilledCallbacks.push(onFULFILLED);this.onRejectedCallbacks.push(onREJECTED);}if (this.status === Commitment.FULFILLED &&typeof onFULFILLED === "function") {// then是异步的setTimeout(() => {// 执行成功的回调onFULFILLED(this.result);});}if (this.status === Commitment.REJECTED &&typeof onREJECTED === "function") {// then是异步的setTimeout(() => {// 执行失败的回调onREJECTED(this.error);});}return this; // 返回当前的promise对象}}
为什么直接返回this可以实现回调呢???
方法调用和返回值
在 JavaScript 中,方法的调用(例如 obj.method()
)会返回方法的返回值。
如果方法返回 this
(即对象本身),那么调用该方法后,你仍然持有对该对象的引用。
链式调用的实现
当你调用
obj.method1().method2()
时:
obj.method1()被调用,并返回
obj(因为
method1返回
this)。
然后
method2()被调用,其调用者仍然是
obj`。
这样,你可以连续调用多个方法,形成链式调用。
**总结:**手写 Promise 的核心在于实现状态管理、异步回调和链式调用。首先,Promise 有三种状态(pending/fulfilled/rejected),通过构造函数接收执行器函数,并用 resolve
和 reject
更新状态。
关键点包括:**状态管理:**初始为 pending
,调用 resolve
或 reject
后不可逆地变为 fulfilled
或 rejected
。异步回调:then
方法需异步执行回调(用 setTimeout
),若状态为 pending
,需将回调存入数组,待状态变更后遍历执行。**错误捕获:**构造函数中用 try/catch
捕获执行器函数的同步错误,并调用 reject
。链式调用:then
返回 this
,使后续 then
能继续绑定回调,形成链式调用。
最终实现需确保:
- 状态变更后异步触发回调。
- 回调参数类型检查(非函数则忽略)。
象的引用。