JavaScript手写Promise

本文深入解析Promise对象的概念,探讨其在异步操作中的角色,以及如何通过then和catch方法处理成功和失败状态。同时,提供了Promise对象的实现代码,帮助读者理解其内部机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Promise 对象用于表示一个异步操作的最终状态(完成或失败),以及其返回的值。

Promise 对象是一个代理对象(代理一个值),被代理的值在Promise对象创建时可能是未知的。它允许你为异步操作的成功和失败分别绑定相应的处理方法(handlers)。 这让异步方法可以像同步方法那样返回值,但并不是立即返回最终执行结果,而是一个能代表未来出现的结果的promise对象

一个 Promise有以下几种状态:
  • pending: 初始状态,既不是成功,也不是失败状态。
  • fulfilled: 意味着操作成功完成。
  • rejected: 意味着操作失败。

pending 状态的 Promise 对象可能触发fulfilled状态并传递一个值给相应的状态处理方法,也可能触发失败状态(rejected)并传递失败信息。当其中任一种情况出现时,Promise 对象的 then 方法绑定的处理方法(handlers )就会被调用(then方法包含两个参数:onfulfilled 和onrejected,它们都是 Function 类型。当Promise状态为fulfilled时,调用 then 的onfulfilled 方法,当Promise状态为rejected时,调用 then 的 onrejected 方法, 所以在异步操作的完成和绑定处理方法之间不存在竞争)。

promise
实现如下:


// 实现Promise

function Promise(executor){

	let self = this;
	// 保存初始化的状态
	self.status  = 'pedding';
	// 接收成功的值
	self.value = undefined;
	// 接收失败的原因
	self.reason = undefined;
	// 存放成功的回调
	self.onResolved = [];
	// 存放失败的回调
	self.onRejected = [];
	function resolve(value) {
		if (self.status === 'pedding') {
			// 将成功的值保存起来
			self.value = value;
			// 成功时改变为成功状态
			self.status = 'fulfilled';
			self.onResolved.forEach(fn => fn());
		}
	}

	function reject(reason) {
		if (self.status === 'pedding') {
			// 将失败的原因存起来
			self.reason = reason;
			// 失败时改变为失败状态
			self.status = 'rejected';

			self.onRejected.forEach(fn => fn());
		}
	}
	// 处理异常
	try {
		executor(resolve, reject);
	}catch(e) {
		reject(e);
	}
	// (resolve, reject)();
}

Promise.prototype.then = function (onfulfilled, onrejected) {
	// 判断是否有相应的回调函数
	// 如果没有成功时的回调默认返回值
	// 如果没有失败时的回调默认抛出异常
	onfulfilled = typeof onfulfilled == 'function' ? onfulfilled : val => val;
	onrejected = typeof onrejected == 'function' ? onrejected : err => {
		throw err;
	};

	let self = this,
		promise2;
	// 实现链式调用
	return promise2 = new Promise((resolve, reject) => {
		// 成功时调用成功的回调
		if (self.status === 'fulfilled') {
			let res = onfulfilled(self.value);
			resolvePromise(promise2, res, resolve, reject);
			// onfulfilled(self.value);
		}
		// 失败时调用失败的回调
		if (self.status === 'rejected') {
			let res = onrejected(self.reason);
			resolvePromise(promise2, res, resolve, reject);
			// onrejected(self.reason);
		}
		// pedding时把成功和失败的回调存入数组中
		if (self.status === 'pedding') {
			self.onResolved.push(function() {
				// onfulfilled(self.value);
				let res  = onfulfilled(self.value);
				resolvePromise(promise2, res, resolve, reject);

			});
			self.onRejected.push(function() {
				// onrejected(self.reason);
				let res = onrejected(self.reason);
				resolvePromise(promise2, res, resolve, reject);

			});
		}
	})
}
// 扩展catch方法
Promise.prototype.catch = function (onrejected) {
  return this.then(null, onrejected)
}
// 扩展reject方法
Promise.reject = function (reason) {
  return new Promise((resolve, reject) => {
    reject(reason)
  })
}
// 扩展resolve方法
Promise.resolve = function (value) {
  return new Promise((resolve, reject) => {
    resolve(value);
  })
}
// 解决回调地狱 处理链式调用问题
function resolvePromise(p2, x, resolve, reject) {
  if(p2 === x){
  	ject(new TypeError('循环引用'));
  }
  if(x!= null && (typeof x === 'object' || typeof x === 'function')){
    try{
      let then = x.then;
      if(typeof then === 'function'){
        then.call(x, (y) => {
        	if (called) return;
        	called = true;
            resolvePromise(promise2, y, resolve, reject);
        }, (err) => {
        	if (called) return ;
        	called = true;
            reject(err);
        });
      }else{
        resolve(x);
      }
    }catch(e){
      reject(e);
    }
  }else{
    resolve(x);
  }
}

运行测试代码如下

let promise = new Promise((resolve,reject)=>{
   resolve(1);
});
console.log('start');
promise.then((value) => {
   console.log(value)
   return value;
}, (reason) => {
  console.log(reason);
		})
.then((data) => {
    	console.log("data"+ data);
    });
console.log('end');

运行结果如下
图片

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值