真的要搞懂Promise的流程啊混蛋

核心概念

实现自己的Promise的重点pedding时缓存回调,变更后异步触发

关键原理

下面是自己实现的Promise

js 复制代码
	class MyPromise {
	  constructor(executor) {
	    this.state = 'pending';
	    this.value = null;
	    this.onFulfilledCallbacks = [];
	    this.onRejectedCallbacks = [];
	
	    const resolve = (value) => {
	      if (this.state !== 'pending') return;
	      this.state = 'fulfilled';
	      this.value = value;
	      this.onFulfilledCallbacks.forEach(fn => fn());
	    };
	
	    const reject = (reason) => {
	      if (this.state !== 'pending') return;
	      this.state = 'rejected';
	      this.value = reason;
	      this.onRejectedCallbacks.forEach(fn => fn());
	    };
	
	    try {
	      executor(resolve, reject); // 同步执行 executor
	    } catch (e) {
	      reject(e);
	    }
	  }
	
	  then(onFulfilled, onRejected) {
	    if (this.state === 'fulfilled') {
	      onFulfilled(this.value);
	    } else if (this.state === 'rejected') {
	      onRejected(this.value);
	    } else { // pending 状态保存回调
	      this.onFulfilledCallbacks.push(() => onFulfilled(this.value));
	      this.onRejectedCallbacks.push(() => onRejected(this.value));
	    }
	  }
	}

然后是调用的例子

js 复制代码
	let aPromise = new MyPromise(reslove => {
		setTimeout(() => {
			reslove('123')
		}, 5000)
	})

如果我们在5秒内输出aPromise,得到的结果是

js 复制代码
	{
		onFulfilledCallbacks: []
		onRejectedCallbacks: []
		state: "pedding"
		value: null
	}

MyPromise这个类的作用就是初始化五秒钟内输出的这样一个对象,这个时候,Mypromise内的参数是同步执行的,但是一个定时器,让我们在五秒钟后才会触发类中的reslove方法,这个时候才会把status改为fulfilled,value改为3,所以我们五秒后如果在输出aPromise,会得到结果

js 复制代码
	{
		onFulfilledCallbacks: []
		onRejectedCallbacks: []
		state: "fulfilled"
		value: '123'
	}

别忘了,我们的MyPromise类中还有一个then的方法,顺着输出的原型链,我们可以看到then的输出,上面代码的实现中我们只是实现了一层的then的方法,真正的promise需要我们在then同样返回一个promise,不过我们先来看看这个简单的实现。 和MyPromise的构造函数类似,在then中,我们传递两个回调函数,并在pedding的时候保存这两个函数,这两个函数的作用就是在state == fulfilled时候调用第一个,也就是成功的回调,当state == reject时候调用第二个,也就是异常的回调。 当然真实的Promise不是这样简单,我们还是要看看如何实现链式调用。

js 复制代码
    then(onFulfilled, onRejected) {
     // 1. 参数标准化(处理值穿透)
     onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v => v;
     onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err };
   
     // 2. 创建新Promise(链式调用基础)
     const promise2 = new MyPromise((resolve, reject) => {
       const handleCallback = (callback) => {
         setTimeout(() => {  // 异步执行(微任务模拟)
           try {
             const x = callback(this.value); 
             resolvePromise(promise2, x, resolve, reject); // 关键!
           } catch (e) {
             reject(e);
           }
         }, 0);
       };
   
       // 3. 状态分派逻辑
       if (this.state === 'fulfilled') handleCallback(onFulfilled);
       else if (this.state === 'rejected') handleCallback(onRejected);
       else { // pending状态时存入队列
         this.onFulfilledCallbacks.push(() => handleCallback(onFulfilled));
         this.onRejectedCallbacks.push(() => handleCallback(onRejected));
       }
     });
   
     return promise2; // 返回新Promise实现链式
   }

链式调用的基础就是在then中返回一个新的Promise,所以我们肯定首先需要的是先创建一个promise用于返回,下面根据state的状态来进行暂存或者调用回调函数是和之前类似的,只是这里我们没有直接去触发回调函数,因为回调函数中从来不是简单的几行赋值,打印的代码,它是有可能包含同样是Promise的代码的,所以then中不但要返回一个新的Promise,还有把可能包含在回调函数中的Promise解析出来,这就是resolvePromise的作用

js 复制代码
    function resolvePromise(promise2, x, resolve, reject) {
     // 1. 循环引用检测(Promise/A+ 2.3.1)
     if (promise2 === x) {
       return reject(new TypeError('Chaining cycle detected'));
     }
   
     // 2. 对象/函数类型处理
     if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
       let called = false; // 防止多次调用
       try {
         const then = x.then;
         if (typeof then === 'function') { // 识别为Promise
           then.call(
             x,
             y => { // 递归解析
               if (called) return;
               called = true;
               resolvePromise(promise2, y, resolve, reject);
             },
             r => { // 直接拒绝
               if (called) return;
               called = true;
               reject(r);
             }
           );
         } else { // 普通对象
           resolve(x);
         }
       } catch (e) {
         if (called) return;
         reject(e);
       }
     } else { // 基本类型直接resolve
       resolve(x);
     }
   }

递归调用,为Promise resolve出最终的结果。 结合下面对应的流程图更好理解

另外

代码中还有一些类型的判断是必须要有的,但是没有去解释,因为这里的重点只是去梳理整个流程,从宏观上掌握实现逻辑

常见问题

图表

flowchart TD A[then方法调用] --> B[参数标准化处理] B --> C[创建新Promise promise2] C --> D[定义handleCallback函数] D --> E[状态判断] E --> F[状态: fulfilled] E --> G[状态: rejected] E --> H[状态: pending] F --> I[异步执行onFulfilled] G --> J[异步执行onRejected] H --> K[回调存入队列] I --> L[resolvePromise处理] J --> L K --> L L --> M[resolvePromise开始] M --> N{返回值x类型判断} N --> O[循环引用检测] O --> P[抛出TypeError] N --> Q[Promise对象] Q --> R[递归调用then方法] R --> L N --> S[Thenable对象] S --> T[调用then方法] T --> U[处理回调结果] U --> L N --> V[普通值] V --> W[直接resolve] W --> X[promise2状态确定] P --> X
相关推荐
Zuckjet2 分钟前
从零到百万:Notion如何用CRDT征服离线协作的终极挑战?
前端
ikonan7 分钟前
译:Chrome DevTools 实用技巧和窍门清单
前端·javascript
Juchecar7 分钟前
Vue3 v-if、v-show、v-for 详解及示例
前端·vue.js
ccc101811 分钟前
通过学长的分享,我学到了
前端
编辑胜编程11 分钟前
记录MCP开发表单
前端
可爱生存报告11 分钟前
vue3 vite quill-image-resize-module打包报错 Cannot set properties of undefined
前端·vite
__lll_11 分钟前
前端性能优化:Vue + Vite 全链路性能提升与打包体积压缩指南
前端·性能优化
weJee12 分钟前
pnpm原理
前端·前端工程化
小高00713 分钟前
⚡️ Vue 3.5 正式发布:10× 响应式性能、SSR 水合黑科技、告别 .value!
前端·javascript·vue.js
乡村中医13 分钟前
🔥如何在函数式编程中使用设计模式-单例模式
前端·代码规范