自己写一个Promise

我们知道,Promise是ECMAScript 6写入了标准的产物(比如IE11及以下版本就不支持),在ECMAScript 5标准中并不支持原生的Promise对象,网上可以找到各种第三方库来获得相应的支持。其实在通过对Promise原理的理解后,自己也可以写一个“类函数”来支持Promise。虽然我们在程序开发中不一定都要自己造轮子,但通过自己造轮子的过程可以加深对原生对象原理的认识和理解。

以下CustomPromise的原码博主自己写的Promise“类函数”,支持原生Promise基本功能及与其相同的“then().then()...”链式调用,但对于【(原生Promise支持的)前一个Promise(在then中指定)的resolve或reject回调执行后,返回的值本身又是一个Promise对象】的情况没做考虑支持(说明:测试代码中使用的箭头函数“=>”属于ES6标准):


//var i=0;
function CustomPromise(fn){
	//debugger; 	
	//region 属性、方法
	//this.name=++i;	//for test
	//this.fn=fn;	
	this.resolveTask=function(arg){	//arg:与原生Promise一样,回调中只能传递一个参数			
		//一个Promise对象中安排一次遇到的resolve或reject任务,先遇到谁就安排谁,后面的忽略
		if (this.hasTaskHandler){
			return;	
		}		
        this.hasTaskHandler=true;
		var self=this;	           
		setTimeout(function(){
			if (self.state=='pending'){
				if (!!self.resolve) {
					try{
						arg=arg||self.arg;
						self.next.arg=self.resolve(arg); //执行的结果作为链中下一节点resolve回调的参数
						self.state='resolved';
					}catch(e){						
						//出错应该在链中传递rejected状态;如果链中都没有reject回调,则抛出异常:
						self.findRejectInChain(e);
					}
				}				
				//else {
					//调用了resolveTask,却没有指定resolve回调,与原生Promise一样不抛异常
				//}				
			}else if (self.state=='rejected'){	//如果等待任务执行的过程出错改变了状态,进入这个条件只有一种情况:即是在链中前方节点执行了findRejectInChain()的过程中修改了此节点的state为“rejected”,这种情况即便没有指定self.reject回调也不应该(像rejectTask中一样)再执行findRejectInChain()方法。
				debugger;
				if (!!self.reject) {
					try{						
						self.next.arg=self.reject(self.arg); //执行的结果作为链中下一节点resolve回调的参数
						self.state='resolved';
					}catch(e){						
						//出错应该在链中传递rejected状态;如果链中都没有reject回调,则抛出异常:
						self.findRejectInChain(e);
					}
				}
			}
		},0);
	};
	
	this.rejectTask=function(arg){	//arg:与原生Promise一样,回调中只能传递一个参数
		//一个Promise对象中安排一次遇到的resolve或reject任务,先遇到谁就安排谁,后面的忽略
		if (this.hasTaskHandler){
			return;	
		}		
		this.hasTaskHandler=true;
        var self=this;	          
		setTimeout(function(){
			if (self.state!='resolved'){
				if (!!self.reject){
					try{						
						self.next.arg=self.reject(arg);	//执行的结果作为链中下一节点resolve回调的参数
						self.state='resolved';
					}catch(e){						
						//出错应该在链中传递rejected状态;如果链中都没有reject回调,则抛出异常:
						self.findRejectInChain(e);
					}
				}else {	//if (!self.reject) 没有指定reject回调
					if (self.state=='rejected' && (arg instanceof Error)) {					
						//出错应该在链中传递rejected状态;如果链中都没有reject回调,则抛出异常:
						self.findRejectInChain(arg);						
					}else{
						//调用了rejectTask,却没有指定reject
						self.findRejectInChain(new Error('缺少reject回调'));	
					}
				}
			}
		},0);		
	};
	
	//在链中传递rejected状态;如果链中都没有reject回调,则抛出异常
	this.findRejectInChain=function(e){
		var chainProm=this.next;
		var hasRejectInChain=false;
		while (!!chainProm){
			chainProm.state='rejected';//后面的链中都不执行resolve回调
			chainProm.arg=e;	//错误e作为后面reject回调的接收参数值
			if (!!chainProm.reject){
				hasRejectInChain=true;
				break;
			}								
			chainProm=chainProm.next;
		}
		if (!hasRejectInChain){
			throw e;
		}			
	};
	
	this.then=function(res,rej){
		if (!res && !rej){
			return this;	//传入空的传数时忽略,但不能断开链
		}else {
			this.resolve=res;		
			this.reject=rej;
			
			//为了支持链式调用这里需要再返回一个新的CustomPromise:	
			var then_fn=function(then_resolve,then_reject){
				then_resolve();
			};
			this.next= new CustomPromise(then_fn);
			return this.next;
		}
	};
	//endregion 属性
	
	//初始化:
    if (fn){
		try{
			this.state='pending';
			fn(this.resolveTask.bind(this),this.rejectTask.bind(this));				
		}catch(e){
			//如果fn执行出错了,且未有安排resolve或reject任务,那么需要调用rejectTask安排reject任务将异常传递下去
			if (!this.hasTaskHandler){
				this.state='rejected';
				this.rejectTask.call(this,e);
			}
		}
    }
}
 
//以下是针对CustomPromise各种情形的测试,结果可以与原生Promise作对比:
debugger; 
//基本流程测试:
var myPromise=new CustomPromise(function(suc,fail){
	var rand=Math.random();
	console.log(rand);
	if(rand>0.7) {
		suc();
		console.log('已安排任务1');
	}else if (rand>0.3){
		suc();
		console.log('已安排任务1,即将出错,但在出错前已经安排任务或异常处理的情况下不再安排异常处理,因此即将进入的是任务1');
		kkksfsf();
	}else{
		fail();
		console.log('已安排处理异常1');
	}
});
myPromise.then(()=>{console.log('成功执行任务1,任务1中将出错,进入异常2');ksdfs();},()=>{console.log('执行处理异常1,将进入任务2');})
.then(()=>{console.log('成功执行任务2,任务2中将出错,进入异常3');p.noAction();},()=>console.log('执行处理异常2,将进入任务3'))
.then(()=>console.log('成功执行任务3'),()=>console.log('执行处理异常3'));
 
 
//以下测试得到与原生Promise一致的结果:
//new CustomPromise((x,y)=>{fsf();x();});	//【测试先执行出错,没有指定resolve和reject】,返回Promise对象,并抛出异常fsf is not defined 
//new CustomPromise((x,y)=>{fsf();x();}).then(()=>{});	//【测试先执行出错,没有指定reject】,返回Promise对象,并抛出异常fsf is not defined
//new CustomPromise((x,y)=>{fsf();x();}).then(()=>{},()=>{});	//【测试先执行出错,reject空函数处理】,返回Promise对象,无异常
//new CustomPromise((x,y)=>{fsf();x();}).then(()=>{}).then(()=>{});	//【测试先执行出错,链式中的节点都没有reject处理】,返回Promise对象,并抛出异常fsf is not defined
//new CustomPromise((x,y)=>{fsf();x();}).then(()=>{}).then(()=>{},()=>{});	//【测试先执行出错,链式中隔了几个节点后reject空函数处理】,返回Promise对象,无异常
//new CustomPromise((x,y)=>{fsf();x();}).then((p1,p2)=>{console.log(1);console.log(2);}).then(()=>{},(y)=>{console.log(3);});	//【测试先执行出错,链式中隔了几个节点后reject处理】,返回Promise对象,输出3

//new CustomPromise((x,y)=>{y();x();}).then((p1)=>{console.log(1);}).then(()=>{},(y)=>{console.log(3);});	//【测试先调用reject,链式中隔了几个节点后reject回调时处理】,输出3
//new CustomPromise((x,y)=>{xsfsf();y();}).then((p1)=>{console.log(1);}).then(()=>{});  //【测试先执行出错,但没有指定reject回调时的错误】,返回Promise对象,并抛出异常Uncaught ReferenceError: xsfsf is not defined
//new CustomPromise((x,y)=>{y();xsfsf();}).then((p1)=>{console.log(1);}).then(()=>{});	//【测试先调用reject,但没有指定reject回调时的错误】,返回Promise对象,并抛出异常Uncaught Error: 缺少reject回调
//new CustomPromise((x,y)=>{x();xsfsf();}).then().then((p1)=>{console.log(1);}).then(()=>{});	//【测试先安排resolve后面错误被忽略,以及then()传空参数维持链式操作】, 返回Promise对象,并输出1
//new CustomPromise((x,y)=>{x(23);xsfsf();}).then().then((p2)=>{console.log(p2);}).then(()=>{});	//【测试then()传空参数不中断链,并且将原来的参数往下传递参数直到有指定回调为止】,返回Promise对象,并输出23 
//new CustomPromise((x,y)=>x(2)).then((p1,p2)=>sfdf()).then(()=>console.log('000')).then(()=>console.log(1),(p2)=>{console.log("1."+p2);return "2."+p2+' resolved';}).then((x)=>console.log(x));//【测试错误作为参数在链中传递给reject回调,reject执行后结果作为后面节点的resolve回调的参数】, 返回Promise对象,并输出1.ReferenceError: sfdf is not defined 2.ReferenceError: sfdf is not defined resolved



/*
//用原生Promise测试,对比结果:
var promise=new Promise(function(suc,fail){
	var rand=Math.random();
	console.log(rand);
	if(rand>0.7) {
		suc();
		console.log('已安排任务1');
	}else if (rand>0.3){
		suc();
		console.log('已安排任务1,即将出错,但在已经安排任务或异常处理的情况下不再安排异常处理,因此即将进入的是任务1');
		kkksfsf();
	}else{
		fail();
		console.log('已安排处理异常1');
	}
});
promise.then(()=>{console.log('成功执行任务1,任务1中将出错,进入异常2');ksdfs();},()=>{console.log('执行处理异常1,将进入任务2');})
.then(()=>{console.log('成功执行任务2,任务2中将出错,进入异常3');p.noAction();},()=>console.log('执行处理异常2,将进入任务3'))
.then(()=>console.log('成功执行任务3'),()=>console.log('执行处理异常3'));
*/

 

以下测试结果也是与预料一致的,可以用原生Promise加以比较验证,得出的也是一致的流程结果。

 

注意说明

1. 作为基础实现,目前CustomPromise中的链式调用只支持处理【前一个Promise(在then中指定)的resolve或reject回调的返回值是普通对象】的情况,对于【(原生Promise支持的)前一个Promise(在then中指定)的resolve或reject回调执行后,返回的值本身又是一个Promise对象】的情况并没有做支持;

2. 以下原生Promise的静态/实例方法在CustomPromise中没做实现,原生Promise及其方法在各个浏览器的支持程度也并不相同,详细可参见浏览器兼容性中的表格说明:

 

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
很高兴为您提供一个Promise 实现,代码如下: class Promise { constructor(executor) { this.state = 'pending'; this.value = undefined; this.reason = undefined; this.onResolvedCallbacks = []; this.onRejectedCallbacks = []; const resolve = (value) => { if (this.state === 'pending') { this.state = 'fulfilled'; this.value = value; this.onResolvedCallbacks.forEach((fn) => fn()); } }; const reject = (reason) => { if (this.state === 'pending') { this.state = 'rejected'; this.reason = reason; this.onRejectedCallbacks.forEach((fn) => fn()); } }; try { executor(resolve, reject); } catch (error) { reject(error); } } then(onFulfilled, onRejected) { onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : (value) => value; onRejected = typeof onRejected === 'function' ? onRejected : (reason) => { throw reason }; const promise2 = new Promise((resolve, reject) => { if (this.state === 'fulfilled') { setTimeout(() => { try { const x = onFulfilled(this.value); Promise.resolvePromise(promise2, x, resolve, reject); } catch (error) { reject(error); } }, 0); } if (this.state === 'rejected') { setTimeout(() => { try { const x = onRejected(this.reason); Promise.resolvePromise(promise2, x, resolve, reject); } catch (error) { reject(error); } }, 0); } if (this.state === 'pending') { this.onResolvedCallbacks.push(() => { setTimeout(() => { try { const x = onFulfilled(this.value); Promise.resolvePromise(promise2, x, resolve, reject); } catch (error) { reject(error); } }, 0); }); this.onRejectedCallbacks.push(() => { setTimeout(() => { try { const x = onRejected(this.reason); Promise.resolvePromise(promise2, x, resolve, reject); } catch (error) { reject(error); } }, 0); }); } }); return promise2; } static resolvePromise(promise2, x, resolve, reject) { if (promise2 === x) { return reject(new TypeError('Chaining cycle detected for promise')); } let called = false; if (x instanceof Promise) { x.then((value) => { Promise.resolvePromise(promise2, value, resolve, reject); }, reject); } else if (x !== null && (typeof x === 'object' || typeof x === 'function')) { try { const then = x.then; if (typeof then === 'function') { then.call(x, (value) => { if (called) return; called = true; Promise.resolvePromise(promise2, value, resolve, reject); }, (reason) => { if (called) return; called = true; reject(reason); }); } else { resolve(x); } } catch (error) { if (called) return; called = true; reject(error); } } else { resolve(x); } } }

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值