ES6 Promise深入理解

一、核心

简要概括:Promise的主要作用是作为一个存储 状态 和 结果 的容器。

1、Promise对象的内部属性变量

本文所说的内部属性变量的意思就是:JS代码中不能通过任何方法获取或者赋值的属性变量。

这种变量在Promise对象中有两个:

1.1、PromiseStatus

用于存储执行状态的变量,其固定只有三种值,代表三种不同的状态。本文称为PromiseStatus变量(Google浏览器控制台上显示的属性变量名)。

  • pending代表进行中的状态,是PromiseStatus变量的初始值,变量值为pending时才能进行修改值
  • resolved代表已成功的状态,只有当PromiseStatus变量为pending的情况下,才能将值变为resolved。变量值为resolved后,不可以在进行修改,代表状态凝固。有的时候也会用fulfilled表示。
  • rejected代表已失败的状态,只有当PromiseStatus变量为pending的情况下,才能将值变为rejected。变量值为rejected后,不可以在进行修改,代表状态凝固。
1.2、PromiseValue

用于存储状态改变后的结果的变量,其值可以使用大部分数据类型作为结果数据值(目前Promise实例对象不可以作为该变量的值)。本文称为PromiseValue变量(Google浏览器控制台上显示的属性变量名)。变量的初始值为undefined,且只有一次修改的机会,就是在PromiseStatus变量的值从pending变为其他状态的时候,同步修改PromiseValue变量的值,这一次的修改后,Promise对象的PromiseValue变量的值就不会在修改了。

注意:这两个变量只能在浏览器的控制台上显示一下,代码中实际上是不能直接获取操作的,只有通过实例对象的原型对象上的某些方法的使用,间接知道这两个变量的存在

2、Promise类构造函数中,两个特殊函数

2.1、RESOLVE函数

将实例对象的PromiseStatus变量的值修改为resolved,同时将调用函数时传入的参数一数据赋值给实例对象的PromiseValue变量。这个函数在本文称为RESOLVE函数

2.2、REJECT函数

将实例对象的PromiseStatus变量的值修改为rejected,同时将调用函数时传入的参数一数据赋值给实例对象的PromiseValue变量。这个函数在本文称为REJECT函数

2.3、调用RESOLVE或REJECT

这两个特殊函数目前只在一个地方可以让开发人员获取到并被调用:

  • 在使用Promise构造函数创建实例对象时,Promise构造函数必须有一个函数对象作为参数一,而在Promise构造函数内部会使用RESOLVE函数作为参数一,REJECT函数作为参数二,去调用 Promise构造函数调用时传入的参数一函数,如此在这个构造函数的参数一函数的函数体中,我们就可以获取并调用RESOLVE函数和REJECT函数

    	// 使用Promise的构造函数时,必须有函数对象作为参数一
    	new Promise(); // Uncaught TypeError: Promise resolver undefined is not a function
    	
    	// Promise构造函数内部先新建了一个Promise实例对象,然后调用了构造函数的参数一函数
    	// 在参数一函数中并没有调用RESOLVE函数或者REJECT函数,
    	// 所以新建实例对象的PromiseStatus变量和PromiseValue变量的值依然为初始值
    	new Promise(function(RESOLVE,REJECT){}); // Promise {<pending>: undefined}
    	
    	// 在Promise构造函数的参数一函数中调用了RESOLVE函数,
    	// 所以新建实例对象的PromiseStatus变量修改值为resolved,PromiseValue变量修改值为1
    	new Promise(function(RESOLVE,REJECT){ RESOLVE(1); }); // Promise {<resolved>: 1}
    	
    	// 在Promise构造函数的参数一函数中调用了REJECTE函数,
    	// 所以新建实例对象的PromiseStatus变量修改值为rejected,PromiseValue变量修改值还值为undefined
    	new Promise(function(RESOLVE,REJECT){ REJECT(); }); // Promise {<rejected>: undefined} 
    	
    	// Promise构造函数的参数一函数的函数体中,RESOLVE函数被调用后,
    	// 新建实例对象的PromiseStatus变量值此时已经修改为resolved,
    	// 接着又调用了REJECT函数,REJECT函数内部发现新建实例对象的PromiseStatus变量值不再是pending后
    	// REJECT函数便不会在继续执行,REJECT函数调用失效
    	new Promise(function(RESOLVE,REJECT){ RESOLVE(26); REJECT(); }); // Promise{<resolved>: 26}
    	
    	// 同理如果先调用了REJECT函数,那么RESOLVE函数的调用就会失效
    	new Promise(function(RESOLVE,REJECT){ REJECT(62); RESOLVE(); }); // Promise{<rejected>: 62}
    

    猜测:RESOLVEREJECT函数是绑定了Promise实例对象后的函数,所以这两个函数只能修改绑定的Promise对象。每个RESOLVEREJECT函数都只属于唯一一个Promise对象的。

    	// 伪代码演示Promise中如何生成和传递RESOLVE和REJECT两个函数
    	var resolve = function(){/** 用于修改Promise实例对象的内部变量的函数 **/};
    	var reject = function(){/** 用于修改Promise实例对象的内部变量的函数 **/};
    	function Promise(argMethod){
    		// 猜测在Promise构造函数内部将resolve和reject函数和this对象,
    		// 通过bind方法绑定生成了RESOLVE和REJECT函数
    		argMethod(resolve.bind(this),reject.bind(this));
    	}
    
    	// 将RESOLVE和REJECT两个函数脱离构造函数去使用
    	var resolve;
    	var reject;
    	var p = new Promise((RESOLVE,REJECT)=>{
    		// 此处的RESOLVE和REJECT只能修改p对象的内容
    		// RESOLVE,REJECT两个函数只属于p对象
    		resolve = RESOLVE;
    		reject = REJECT;
    	});
    	p; // Promise {<pending>: undefined}
    	resolve(123);
    	p; // Promise {<resolved>: 123}
    
  • 猜测:then方法内部会创建一个新Promis对象,且会将属于这个对象的RESOLVE或者REJECT两个函数与then方法的两个参数函数绑定在一起作为一个个微任务。当调用then方法的实例对象的状态不在处于pending时,会将对应状态的微任务加入微任务队列中进行执行,具体的代码逻辑可以参考底部对Promise类还原的代码中then方法的部分

2.4、调用时传递的参数

RESOLVEREJECT两个函数在调用时可以传递大部分的数据,其内部猜测都是直接对新建实例对象的PromiseStatus变量和PromiseValue变量进行修改,将两个函数的参数直接赋值给PromiseValue变量,然后根据调用的是哪个函数,将PromiseStatus变量修改为对应的状态。

但是也有特例的情况,如果传递的参数是Promise实例对象的话,或者是一个包含then方法的对象,RESOLVE函数内部的逻辑就不再是直接修改PromiseStatus变量和PromiseValue变量了,具体逻辑可以参考底部对Promise类还原的代码中amendPromiseResolved或者amendPromiseRejected方法的部分(但是REJECT函数,依然直接修改Promise对象的内部变量)

2.5、细节

当我们调用了RESOLVEREJECT函数后,并不代表Promise构造函数的参数函数已经执行完了,我们还可以继续执行其他代码,但是不推荐这样写

	new Promise((RESOLVE,REJECT)=>{
		console.log('调用RESOLVE');
		RESOLVE(123);
		console.log('调用RESOLVE后,并不代表终结');
	})
	// 控制输出打印: 调用RESOLVE 		调用RESOLVE后,并不代表终结

RESOLVEREJECT函数在被调用时的实质只是修改了PromiseStatus变量和PromiseValue变量,然后在判断下Promise实例对象中是否记录了微任务,如果有记录就把微任务添加进微任务队列。

其内部并没有涉及到终结函数的内容,所以在RESOLVEREJECT函数调用后,依然可以继续执行接下来的代码,直到代码全部执行完毕。

但是最好在RESOLVEREJECT函数被调用后,就不要在有代码执行了,用return替代

	new Promise((RESOLVE,REJECT)=>{
		console.log('调用RESOLVE');
		RESOLVE(123);
		return;
	})

3、Promise.prototype.then

then方法是用来获取Promise对象的PromiseStatusPromiseValue的途径。

PromiseStatus变量的值是不可以直接获取的,只有通过then方法的参数函数被调用时,才能间接知道PromiseStatus变量目前是哪个值:

var p1 = new Promise((RESOLVE,REJECT)=>{
	if((new Date().getTime()%2)===0){
		RESOLVE('当前时间戳是偶数');
	}else{
		REJECT('当前时间戳是奇数');
	}
});
p1.then(
	(resolvedResult)=>{/** 当本函数被调用时说明p1对象的PromiseStatus变量的值是resolved **/},
	(rejectedResult)=>{/** 当本函数被调用时说明p1对象的PromiseStatus变量的值是rejected **/}
);

PromiseValue变量的值是可以通过then方法的参数函数的回调获取到的。且then方法的参数函数被调用时所用的参数与Promise对象上的PromiseValue变量的值是同一个数据:

var evenNumber = {desc:'偶数'};
var oddNumber = {desc:'奇数'};
var p1 = new Promise((RESOLVE,REJECT)=>{
	if((new Date().getTime()%2)===0){
		RESOLVE(evenNumber);
	}else{
		REJECT(oddNumber);
	}
});
p1.then(
	(resolvedResult)=>{
		// resolvedResult就是p1对象的状态为resolved时,PromiseValue变量的值
		console.log(resolvedResult===evenNumber); // 控制台输出打印 true
		return resolvedResult;
	},
	(rejectedResult)=>{
		// rejectedResult就是p1对象的状态为rejected时,PromiseValue变量的值
		console.log(rejectedResult===oddNumber);	// 控制台输出打印 true
		return rejectedResult;
	}
);

then方法会返回一个新建的Promise对象,这个对象是在then方法内部创建的,且内部变量的初始值分别是 pendingundefined,当这个新建Promise对象经过微任务的操作后,其内部变量才会发生改变:

	var p = new Promise((RESOLVE,REJECT)=>{
		RESOLVE(321);
	})
	// then方法中因为此时 p 的状态不是 pending ,所以将一个改变p1内部变量的微任务加入到了微任务队列中 
	var p1 = p.then(
  		(resolveResult)=>{ return '已成功' },
		(rejectResult)=>{ return '已失败' }
	);
	p1; // 此时宏任务没有结束 p 的内部变量的值分别是 pending 和 undefined
	debugger; // 使用断点让宏任务停在这里
	// 当宏任务结束后,微任务也执行完,此时p1的内部变量发生了改变
	p1; // p1内部变量分别是 resolved 和 '已成功' 

调用then方法时会传递两个参数函数,当调用then方法的对象的状态不在是pending时,那对应状态的参数函数会被调用,且会拿参数函数返回的数据作为参考去修改then方法返回Promise对象的内部变量:

var p1 = new Promise((RESOLVE,REJECT)=>{
	RESOLVE(123);
})
var p2 = new Promise((RESOLVE,REJECT)=>{
	REJECT(321);
})
var p3 = p1.then((x)=>{
	console.log(x); // 输出打印 123
	// 如果p1的状态是resolved,那么then参数一函数会被调用。
	// 此处参数一函数的返回值是 undefined
	// 所以 p3 最终修改内部变量后,其值分别是:
	// resolved(因为在调用参数一函数时,函数没有抛出异常,所以PromiseStatus直接修改为resolved)
	// undefined(因为在调用参数一函数时,函数没有抛出异常,所以PromiseValue以参数一函数的返回值作为其值)
},null); // 内部变量的值分别是 pending 和 undefined
var p4 = p1.then((x)=>{
	console.log(x); // 输出打印 123
	// 如果p1的状态是resolved,那么then参数一函数会被调用。
	// 此处参数一函数的返回值是 hehehe,
	// 所以 p4 最终修改内部变量后,其值分别是:
	// resolved(因为在调用参数一函数时,函数没有抛出异常,所以PromiseStatus直接修改为resolved)
	// hehehe(因为在调用参数一函数时,函数没有抛出异常,所以PromiseValue以参数一函数的返回值作为其值)
	return 'hehehe'
},null); // 内部变量的值分别是 pending 和 undefined
var p5 = p2.then(null,(x)=>{
	console.log(x); // 输出打印 321
	// 如果p2的状态是rejected,那么then参数二函数会被调用。
	// 此处参数二函数的返回值是 undefined
	// 所以 p5 最终修改内部变量后,其值分别是:
	// resolved(因为在调用参数二函数时,函数没有抛出异常,所以PromiseStatus直接修改为resolved)
	// undefined(因为在调用参数二函数时,函数没有抛出异常,所以PromiseValue以参数二函数的返回值作为其值)
}); // 内部变量的值分别是 pending 和 undefined
var p6 = p2.then(null,(x)=->{
	console.log(x); // 输出打印 321
	// 如果p2的状态是rejected,那么then参数二函数会被调用。
	// 此处参数二函数的返回值是 zezeze
	// 所以 p6 最终修改内部变量后,其值分别是:
	// resolved(因为在调用参数二函数时,函数没有抛出异常,所以PromiseStatus直接修改为resolved)
	// undefined(因为在调用参数二函数时,函数没有抛出异常,所以PromiseValue以参数二函数的返回值作为其值)
	return 'zezeze'
}); // 内部变量的值分别是 pending 和 undefined
var p7 = p1.then((x)=>{
	console.log(x); // 输出打印 123
	// 不管是参数一还是参数二函数返回值是Promise对象的情况下
	// 那p7内部变量的修改都不在受p1以及then方法的参数函数影响了,
	// p7内部变量的修改受到此处return返回的Promise对象的影响。
	// 如果此处return返回的Promise对象的状态是resolved,那p7的状态就直接修改为resolved
	// 如果此处return返回的Promise对象的状态是rejected,那p7的状态就直接修改为rejected
	// 而p7的PromiseValue变量的值则直接使用return返回的Promise对象的值
	return new Promise((RESOLVE,REJECT)=>{
		REJECT('ERROR');
	});
},null); // 内部变量的值分别是 pending 和 undefined
var p8 = p2.then(null,(x)=->{
	console.log(x); // 输出打印 321
	// 不管是参数一还是参数二函数返回值是Promise对象的情况下
	// 那p8内部变量的修改都不在受p2以及then方法的参数函数影响了,
	// p8内部变量的修改受到此处return返回的Promise对象的影响。
	// 如果此处return返回的Promise对象的状态是resolved,那p8的状态就直接修改为resolved
	// 如果此处return返回的Promise对象的状态是rejected,那p8的状态就直接修改为rejected
	// 而p8的PromiseValue变量的值则直接使用return返回的Promise对象的值
	return new Promise((RESOLVE,REJECT)=>{
		RESOLVE('SUCCESS');
	});
}); // 内部变量的值分别是 pending 和 undefined
debugger; // 使用断点让宏任务停在这里
// 当宏任务结束后,微任务也执行完
p3; // 内部变量的值分别是 resolved 和 undefined
p4; // 内部变量的值分别是 resolved 和 hehehe
// 即使p2的状态是rejected,但是通过p2调用then方法返回的新建实例对象状态也只会是resolved(如果不抛出错误的话)
p5; // 内部变量的值分别是 resolved 和 undefined
p6; // 内部变量的值分别是 resolved 和 zezeze
p7; // 内部变量的值分别是 rejected 和 ERROR
p8; // 内部变量的值分别是 resolved 和 SUCCESS

参数函数之所以会被调用,则是因为在then方法内部会以参数函数为要素形成新的微任务,在then方法中并不会直接调用参数函数。当调用then方法的对象的状态不在是pending时,就会将这些微任务加入到微任务队列中,微任务执行后,参数函数也就间接调用了。
如果调用then方法时,没有传递参数函数,then方法内部就会以调用then方法的对象为要素形成新的微任务。
这就会导致没有参数函数的时候,then方法返回的新建Promise对象的内部变量的修改由调用hen方法的对象来决定。

var p1 = new Promise((RESOLVE,REJECT)=>{
	RESOLVE(123);
})
var p2 = new Promise((RESOLVE,REJECT)=>{
	REJECT(321);
})
var p3 = p1.then(null,()=>{ return '已失败' }); // 内部变量的值分别是 pending 和 undefined
var p4 = p2.then(()=>{ return '已成功' },null); // 内部变量的值分别是 pending 和 undefined
// 产生p5的then方法和产生p3的then方法效果是一样的,都是想使用参数一函数形成的微任务,
// 但是参数一函数没有传递,所以then方法内部使用p1形成微任务,p3和p5内部变量的修改就由p1决定了
// p1的内部变量是多少,那p3和p5的内部变量就是多少
var p5 = p1.then(); // 内部变量的值分别是 pending 和 undefined
// 产生p6的then方法和产生p4的then方法效果是一样的,都是想使用参数二函数形成微任务,
// 但是参数二函数没有传递,所以then方法内部使用p2形成微任务,p4和p6内部变量的修改就由p2决定了
// p2的内部变量是多少,那p4和p6的内部变量就是多少
var p6 = p2.then(); // 内部变量的值分别是 pending 和 undefined
debugger; // 使用断点让宏任务停在这里
// 当宏任务结束后,微任务也执行完,此时p3,p4,p5,p6的内部变量发生了改变
p3; // p3内部变量分别是 resolved 和 123
p4; // p4内部变量分别是 rejected 和 321
p5; // p5内部变量分别是 resolved 和 123
p6; // p6内部变量分别是 rejected 和 321
3.1、then方法的本质

then方法的内部是创建一个新的Promise实例对象,并将属于新建Promise对象的RESOLVE和REJECT函数调用then方法的Promise对象then方法传入的参数一函数和参数二函数这三个要素绑定合成了一个个微任务,接着判断调用then方法的Promis对象的状态处于哪个阶段,根据不同的状态,来决定对合成的微任务进行什么操作。

then方法内部的逻辑伪代码可以参考底部对Promise类还原的代码中then方法的部分

4、Promise中抛出错误

正常情况下,在JS代码中抛出错误,如果不捕捉抛出的错误,那代码执行会停在错误抛出处,不在继续执行。

// 未捕获抛出的错误
console.log('代码执行中,准备抛出错误'); // 输出打印了
throw '抛出一个错误,代码执行停止';
console.log('错误抛出后的代码不会在被执行'); // 没有执行

// 捕获抛出的错误
console.log('代码执行中,准备抛出错误'); // 输出打印了
try{
	throw '抛出一个错误,代码执行停止';
	console.log('错误抛出后的代码不会在被执行'); // 没有执行
}catch(error){
	console.log('打印抛出的错误的信息:' + error);
}
console.log('虽然错误抛出后的代码依然不会在被执行,但是try/catch之后的代码可以执行了'); // 输出打印了

但是在Promise构造函数的参数函数以及then方法的参数函数,如果抛出了错误,这个错误并不会影响到Promise外的代码执行。猜测在Promise内部已经对这些抛出的错误进行了捕获。

4.1、构造函数的参数函数中抛出错误

如果在构造函数的参数函数调用时抛出了错误,那么参数函数中的函数体执行代码会停在错误抛出处,并且直接修改构造函数所创建的Promise对象的PromiseStatus变量值为rejectedPromiseValue变量的值为抛出的错误
猜测:构造函数的参数函数有可能是在try/catch中被调用的,当构造函数的参数函数在被调用时,出现错误后,会对错误进行捕获,并直接拿捕获的错误作为调用REJECT函数的参数。

var p = new Promise(()=>{
	console.log('代码执行中,准备抛出错误'); // 输出打印了
	throw '抛出一个错误,代码执行停止';
	console.log('错误抛出后的代码不会在被执行'); // 没有执行
});
p; // 内部变量的值分别是:rejected 和 抛出的错误
console.log('Promise外的代码执行依然正常'); // 输出打印了

var p1 = new Promise(()=>{
	console.log('代码执行中,准备抛出错误'); // 输出打印了
	// 如果在参数函数中手动捕获抛出的错误,那按照没有错误抛出的逻辑了
	try{
		throw '抛出一个错误,代码执行停止';
	}catch(error){}
	console.log('错误抛出后的代码不会在被执行'); // 没有执行
});
p1; // 内部变量的值分别是:pending 和 undefined

// 抛出错误的时候会调用REJECT函数,但是在抛出错误之前就已经调用RESOLVE或者REJECT函数的话,
// 那因为抛出错误而调用的REJECT函数就会无效
var p2 = new Promise((RESOLVE,REJECT)=>{
	console.log('代码执行中,准备抛出错误'); // 输出打印了
	RESOLVE('已成功')
	throw '抛出一个错误,代码执行停止';
	console.log('错误抛出后的代码不会在被执行'); // 没有执行
});
p2; // 内部变量的值分别是:resolved 和 已成功
var p3 = new Promise((RESOLVE,REJECT)=>{
	console.log('代码执行中,准备抛出错误'); // 输出打印了
	REJECT('已失败');
	throw '抛出一个错误,代码执行停止';
	console.log('错误抛出后的代码不会在被执行'); // 没有执行
});
p3; // 内部变量的值分别是:reject 和 已失败

var p4 = new Promise((RESOLVE,REJECT)=>{
	RESOLVE('OK');
	// 注意:此处抛出异常的地方是在延时函数中,即使延时是0,但是此处setTimeout函数的作用是将延时函数记录下来,
	// 等待下次宏任务执行延时函数。而延时函数执行的地方就不在是Promise构造函数的参数函数中了,
	// 所以其抛出的错误不会被Promise所捕获。
	setTimeout(
		()=>{ throw new Error('test'); },
	0);
});
p4.then((x)=>{ console.log(x); });
// 当 当前宏任务 执行完后,执行微任务,输出打印:ok
// 微任务执行完后,继续执行宏任务,延时函数被调用。
4.2、then方法的参数函数中抛出错误

如果在then方法的参数函数调用时抛出了错误,那么参数函数中的函数体执行代码会停在错误抛出处,并且直接修改then方法所创建的Promise对象的PromiseStatus变量值为rejectedPromiseValue变量的值为抛出的错误
猜测:then方法的参数函数有可能是在try/catch中被调用的,当then方法的参数函数在被调用时,出现错误后,会对错误进行捕获,并直接拿捕获的错误作为调用REJECT函数的参数。

var p1 = new Promise((RESOLVE,REJECT)=>{RESOLVE('已成功');});
var p11 = p1.then((resolveResult)=>{
	console.log('代码执行中,准备抛出错误'); // 输出打印了
	throw 'p1的then的参数函数抛出一个错误,代码执行停止';
	console.log('错误抛出后的代码不会在被执行'); // 没有执行
},null);
p11; // 内部变量的值分别是 pending 和 undefined
var p2 = new Promise((RESOLVE,REJECT)=>{REJECT('已失败');});
var p22 = p2.then(null,(RejectResult)=>{
	console.log('代码执行中,准备抛出错误'); // 输出打印了
	throw 'p2的then的参数函数抛出一个错误,代码执行停止';
	console.log('错误抛出后的代码不会在被执行'); // 没有执行
});
p22; // 内部变量的值分别是 pending 和 undefined
var p23 = p2.then(null,(RejectResult)=>{
	console.log('代码执行中,准备抛出错误'); // 输出打印了
	// 如果在参数函数中手动捕获抛出的错误,那按照没有错误抛出的逻辑了
	try{
		throw 'p2的then的参数函数抛出一个错误,代码执行停止';
	}catch(error){}
	console.log('错误抛出后的代码不会在被执行'); // 没有执行
});
p23; // 内部变量的值分别是 pending 和 undefined
debugger; // 使用断点让宏任务停在这里
p11; // 内部变量的值分别是:rejected 和 p1的then的参数函数抛出一个错误,代码执行停止
p22; // 内部变量的值分别是:rejected 和 p2的then的参数函数抛出一个错误,代码执行停止
p23; // 内部变量的值分别是:resolved 和 undefined

二、Promise余下内容

1、Promise.prototype.catch

Promise对象调用catch方法等价于调用then方法只传参数二,参数一恒定是null

var p = new Promise((RESOLVE,REJEC)=>{ REJEC('已失败'); });
p.catch((rejectResult)=>{console.log(rejectResult);}); 
// 上下两行代码效果等价
p.then(null,(rejectResult)=>{console.log(rejectResult);});

2、Promise.prototype.finally

Promise对象调用finally方法的效果用then方法也可以实现。

var p = new Promise((RESOLVE,REJEC)=>{ REJEC('已失败'); });
var finalFun = function(){
	console.log('finally的回调函数');
}
p.finally(()=>{ finalFun(); });
// 上下两种代码效果等价
p.then(
	(resolvedResult)=>{ 
		finalFun(); 
		return resolvedResult; 
	},
	(rejectResult)=>{ 
		finalFun(); 
		throw rejectResult; 
	}
);

3、Promise.resolve

resolve方法是属于Promise类的静态方法,方法内效果等价于,用new关键字创建了一个新的Promise对象,并且在构造函数的参数函数中立即调用了RESOLVE函数,RESOLVE函数的参数是resolve静态方法的参数,并以新建Promise对象的作为方法的返回值。

Promis.resolve('HeHeHe');
// 上下两种代码效果等价
new Promise((RESOLVE,REJECT)=>{
	RESOLVE('HeHeHe');
});

4、Promise.reject

reject方法是属于Promise类的静态方法,方法内效果等价于,用new关键字创建了一个新的Promise对象,并且在构造函数的参数函数中立即调用了REJECT函数,REJECT函数的参数是reject静态方法的参数,并以新建Promise对象的作为方法的返回值。

Promis.reject('HeHeHe');
// 上下两种代码效果等价
new Promise((RESOLVE,REJECT)=>{
	REJECT('HeHeHe');
});

5、Promise.all

all方法的特性:

  • all方法是属于Promise类的静态方法,方法在调用的时候需要的是一个具有Iterator接口的参数。
  • all方法会返回一个Promise对象,这个对象是在all方法中新建的。
  • all方法会遍历参数中的所有成员,且会使用Promise.resolve()方法将每个成员转为一个Promise对象
  • all方法中新建的Promise对象刚返回时只是一个初始状态,其PromiseStatusPromiseValue变量的改变由all方法的参数来决定:
    1、all方法的参数中的每个成员对应的Promise对象的状态都为reoslved时。all返回的Promise对象的PromiseStatus变量值改为resolvedPromiseValue变量的值改为一个数组,数组中存储的都是all方法参数中每个成员对应Promise对象的回调函数的返回值。
    2、all方法的参数中的每个成员对应的Promise对象中出现第一个状态为rejectedPromise对象A时。all返回的Promise对象的PromiseStatus变量值改为rejectedPromiseValue变量的值改为对象A的回调函数的返回值。

all方法内部代码逻辑可以参考底部Promis类.

6、Promise.race

race方法的特性:

  • race方法是属于Promise类的静态方法,方法在调用的时候需要的是一个具有Iterator接口的参数。
  • race方法会返回一个Promise对象,这个对象是在race方法中新建的。
  • race方法会遍历参数中的所有成员,且会使用Promise.resolve()方法将每个成员转为一个Promise对象
  • race方法中新建的Promise对象刚返回时只是一个初始状态,其PromiseStatusPromiseValue变量的改变由race方法的参数来决定:当race方法的参数中的所有成员所对应的Promise对象中出现第一个状态不在是pending的对象A时,将race方法返回的Promise对象的PromiseStatus变量的值改为对象A的状态,PromiseValue变量的值改为对象A的PromiseValue变量的值

race方法内部代码逻辑可以参考底部Promis类.

Promise微任务何时调用

Promise的微任务一般都是有then方法内部产生并被记录下来的,其在两个地方会被加入微任务队列中,当前宏任务执行完毕后,才会去执行微任务队列中的所有微任务。

  • 在调用then方法时,Promise对象的状态已经不是pending了,此时在then方法中会将刚记录的微任务加入到队列中,但是如果对象状态还处于pending,那么只会记录,不会加入队列。
  • Promise对象改变pending状态为其他状态时,会从记录中取出对应状态的微任务加入到队列中。
	console.log('宏任务1---开始执行');

	var p1 = new Promise(function(RESOLVE,REJECT){
		console.log('宏任务1---创建了Promise实例对象 p1');

		console.log('宏任务1---注册了延时回调函数。注意:回调函数中的函数体执行代码为一个宏任务');
		setTimeout(function(){
			console.log('***********************************');
			console.log('2秒时间到了,将宏任务2加入宏任务队列');
			console.log('***********************************');

			console.log('宏任务2---开始执行');

			console.log('宏任务2---修改Promise实例对象 p1 的状态为 resolved');
			RESOLVE(123);
			console.log('宏任务2---当 p1 的状态变为 resolved,时发现 p1 已经提前注册了回调函数,');
			console.log('宏任务2---所以要从所有的回调函数中筛选出符合的回调要求的函数,将筛选出来的函数的函数体执行代码作为微任务加入到微任务队列中');
			console.log('宏任务2---将p1的回调函数1的函数体执行代码作为宏任务2后的微任务1加入微任务队列中');
			console.log('宏任务2---将p1的回调函数2的函数体执行代码作为宏任务2后的微任务2加入微任务队列中');
			console.log('宏任务2---将p1的回调函数5的函数体执行代码作为宏任务2后的微任务3加入微任务队列中');

			console.log('宏任务2---执行结束');
		},2000);
	});
	var p2 = new Promise(function(RESOLVE,REJECT){
		console.log('宏任务1---创建了Promise实例对象 p2');

		console.log('宏任务1---注册了延时回调函数。注意:回调函数中的函数体执行代码为一个宏任务');
		setTimeout(function(){
			console.log('***********************************');
			console.log('4秒时间到了,将宏任务3加入宏任务队列');
			console.log('***********************************');

			console.log('宏任务3---开始执行');

			console.log('宏任务3---修改Promise实例对象 p2 的状态为 rejected');
			REJECT(321);
			console.log('宏任务3---当 p2 的状态变为 rejected,时发现 p2 已经提前注册了回调函数,');
			console.log('宏任务3---所以要从所有的回调函数中筛选出符合的回调要求的函数,将筛选出来的函数的函数体执行代码作为微任务加入到微任务队列中');
			console.log('宏任务3---将p2的回调函数1的函数体执行代码作为宏任务3后的微任务1加入微任务队列中');
			console.log('宏任务3---将p2的回调函数3的函数体执行代码作为宏任务3后的微任务2加入微任务队列中');
			console.log('宏任务3---将p2的回调函数4的函数体执行代码作为宏任务3后的微任务3加入微任务队列中');
			console.log('宏任务3---将p2的回调函数5的函数体执行代码作为宏任务3后的微任务4加入微任务队列中');

			console.log('宏任务3---执行结束');
		},4000);
	});
	var p3 = new Promise(function(RESOLVE,REJECT){
		console.log('宏任务1---创建了Promise实例对象 p3');

		console.log('宏任务1---修改修改Promise实例对象 p3 的状态为 resolved');
		RESOLVE('ABC');
	});
	var p4 = new Promise(function(RESOLVE,REJECT){
		console.log('宏任务1---创建了Promise实例对象 p4');

		console.log('宏任务1---修改修改Promise实例对象 p4 的状态为 rejected');
		REJECT('CBA');
	});
	
	// 因为此时 p1 的状态是pending,所以给p1注册的回调函数都是先记录下来的
	console.log('宏任务1---给 p1 注册finally回调函数,记录为p1回调函数1');
	p1.finally(()=>{ 
		console.log('宏任务2后的微任务1---开始执行'); 
		console.log('宏任务2后的微任务1---(执行中)')
		console.log('宏任务2后的微任务1---执行结束'); 
	});
	console.log('宏任务1---给 p1 注册then回调函数,参数一记录为p1回调函数2,参数二记录为p1回调函数3');
	p1.then(
		(resolveResult) => {
			console.log('宏任务2后的微任务2---开始执行'); 
			console.log('宏任务2后的微任务2---(执行中)')
			console.log('宏任务2后的微任务2---执行结束'); 
		},
		(rejectResult) => {
			console.log('p1的状态不是rejected,所以这个函数不会执行'); 				
		}
	);
	console.log('宏任务1---给 p1 注册catch回调函数,记录为p1的回调函数4');
	p1.catch((rejectResult)=>{
		console.log('p1的状态不是rejected,所以这个函数不会执行'); 			
	});
	console.log('宏任务1---给 p1 注册finally回调函数,记录为p1回调函数5');
	p1.finally(()=>{
		console.log('宏任务2后的微任务3---开始执行'); 
		console.log('宏任务2后的微任务3---(执行中)')
		console.log('宏任务2后的微任务3---执行结束'); 	

		console.log('***********************************');
		console.log('这时宏任务2后的所有微任务执行完毕');
		console.log('***********************************');	
	});

	// 因为此时 p2 的状态是pending,所以给p2注册的回调函数都是先记录下来的
	console.log('宏任务1---给 p2 注册finally回调函数,记录为p2回调函数1');
	p2.finally(()=>{ 
		console.log('宏任务3后的微任务1---开始执行'); 
		console.log('宏任务3后的微任务1---(执行中)')
		console.log('宏任务3后的微任务1---执行结束'); 
	});
	console.log('宏任务1---给 p2 注册then回调函数,参数一记录为p2回调函数2,参数二记录为p2回调函数3');
	p2.then(
		(resolveResult) => {
			console.log('p4的状态不是resolved,所以这个函数不会执行'); 	
		},
		(rejectResult) => {
			console.log('宏任务3后的微任务2---开始执行'); 
			console.log('宏任务3后的微任务2---(执行中)')
			console.log('宏任务3后的微任务2---执行结束'); 			
		}
	);
	console.log('宏任务1---给 p2 注册catch回调函数,记录为p2的回调函数4');
	p2.catch((rejectResult)=>{
		console.log('宏任务3后的微任务3---开始执行'); 
		console.log('宏任务3后的微任务3---(执行中)')
		console.log('宏任务3后的微任务3---执行结束'); 	
	});
	console.log('宏任务1---给 p2 注册finally回调函数,记录为p2回调函数5');
	p2.finally(()=>{
		console.log('宏任务3后的微任务4---开始执行'); 
		console.log('宏任务3后的微任务4---(执行中)')
		console.log('宏任务3后的微任务4---执行结束'); 		

		console.log('***********************************');
		console.log('这时宏任务3后的所有微任务执行完毕');
		console.log('***********************************');	
	});

	// 因为此时 p3 的状态为 resolved ,所以给 p3 注册的回调函数在注册完成后,会自动将函数体执行代码可以作为微任务添加到微任务队列中,
	// 当宏任务1执行完毕后,就可以一一执行微任务队列中的任务了
	console.log('宏任务1---给 p3 注册finally回调函数。');
	p3.finally(()=>{ 
		console.log('宏任务1后的微任务1---开始执行'); 
		console.log('宏任务1后的微任务1---(执行中)')
		console.log('宏任务1后的微任务1---执行结束'); 
	});
	console.log('宏任务1---因为此时 p3 的状态不是pending,所以将刚注册的回调函数的函数体执行代码作为宏任务1后的微任务1添加进微任务队列。');
	console.log('宏任务1---给 p3 注册then回调函数。');
	p3.then(
		(resolveResult) => {
			console.log('宏任务1后的微任务2---开始执行'); 
			console.log('宏任务1后的微任务2---(执行中)')
			console.log('宏任务1后的微任务2---执行结束'); 
		},
		(rejectResult) => {
			console.log('p3的状态不是rejected,所以这个函数不会执行'); 		
		}
	);
	console.log('宏任务1---因为此时 p3 的状态是resolved,所以将刚注册的then方法的参数一回调函数的函数体执行代码作为宏任务1后的微任务2添加进微任务队列。');
	console.log('宏任务1---给 p3 注册catch回调函数。因为 p3 状态为resolved,所以catch函数的参数一不会被执行');
	p3.catch((rejectResult)=>{
		console.log('p3的状态不是rejected,所以这个函数不会执行'); 	
	});
	console.log('宏任务1---给 p3 注册finally回调函数。');
	p3.finally(()=>{
		console.log('宏任务1后的微任务3---开始执行'); 
		console.log('宏任务1后的微任务3---(执行中)')
		console.log('宏任务1后的微任务3---执行结束'); 
	});
	console.log('宏任务1---因为此时 p3 的状态不是pending,所以将刚注册的回调函数的函数体执行代码作为宏任务1后的微任务3添加进微任务队列。');


	// 因为此时 p4 的状态为 rejected ,所以给 p4 注册的回调函数在注册完成后,会自动将函数体执行代码可以作为微任务添加到微任务队列中,
	// 当宏任务1执行完毕后,就可以一一执行微任务队列中的任务了
	console.log('宏任务1---给 p4 注册finally回调函数。');
	p4.finally(()=>{ 
		console.log('宏任务1后的微任务4---开始执行'); 
		console.log('宏任务1后的微任务4---(执行中)')
		console.log('宏任务1后的微任务4---执行结束'); 
	});
	console.log('宏任务1---因为此时 p4 的状态不是pending,所以将刚注册的回调函数的函数体执行代码作为宏任务1后的微任务4添加进微任务队列。');
	console.log('宏任务1---给 p4 注册then回调函数。');
	p4.then(
		(resolveResult) => {
			console.log('p4的状态不是resolved,所以这个函数不会执行'); 	
		},
		(rejectResult) => {
			console.log('宏任务1后的微任务5---开始执行'); 
			console.log('宏任务1后的微任务5---(执行中)')
			console.log('宏任务1后的微任务5---执行结束'); 
		}
	);
	console.log('宏任务1---因为此时 p4 的状态是rejected,所以将刚注册的then方法的参数二回调函数的函数体执行代码作为宏任务1后的微任务5添加进微任务队列。');
	console.log('宏任务1---给 p4 注册catch回调函数。');
	p4.catch((rejectResult)=>{ 
		console.log('宏任务1后的微任务6---开始执行'); 
		console.log('宏任务1后的微任务6---(执行中)')
		console.log('宏任务1后的微任务6---执行结束'); 
	});
	console.log('宏任务1---因为此时 p4 的状态是rejected,所以将刚注册的回调函数的函数体执行代码作为宏任务1后的微任务6添加进微任务队列。');
	console.log('宏任务1---给 p4 注册finally回调函数。');
	p4.finally(()=>{
		console.log('宏任务1后的微任务7---开始执行'); 
		console.log('宏任务1后的微任务7---(执行中)')
		console.log('宏任务1后的微任务7---执行结束'); 

		console.log('***********************************');
		console.log('这时宏任务1后的所有微任务执行完毕');
		console.log('***********************************');
	});
	console.log('宏任务1---因为此时 p4 的状态不是pending,所以将刚注册的回调函数的函数体执行代码作为宏任务1后的微任务7添加进微任务队列。');

	console.log('宏任务1---执行结束');

Promise类

这里的代码只是还原Promise类内部的部分逻辑的伪代码。

 /* 
 * 修改 Promise对象 的 PromiseStatus 变量为 resolved 
 * 修改 Promise对象 的 PromiseValue 变量为 方法的参数的值 
 */
let amendPromiseResolved = function(resolvedValue){
	// 只有当 Promise对象 的 状态 是 pending 时才可以修改
	if (this['PromiseStatus']==='pending') {
		// 如果 resolvedValue 参数的值是一个 Promise对象 时,需要等这个对象的状态不处于 pending 时,
		// 使用它改变后的 PromiseStatus 和 PromiseValue 变量的值 作为修改的参考
		if (resolvedValue instanceof Promise &&
			Object.prototype.toString.call(resolvedValue).indexOf('Promise')!=-1) {

			// 等待状态不在是 pending 的 Promise对象
			let waitChangedPromise = resolvedValue;
			// 等待 waitChangedPromise 状态不在是 pending 时,进行修改的 Promise对象
			let self = this;
			
			waitChangedPromise.then(
				function(resolvedResult){
					// 当 waitChangedPromise 对象的状态处于 resolved 时,本函数被调用,
					// 此时将 self 对象的 PromiseStatus 变量的值改为 resolved ,
					// self 的 PromiseValue 变量的值改为 resolvedResult
					amendPromiseResolved.bind(self,resolvedResult)();
				},
				function(rejectedResult){
					// 当 waitChangedPromise 对象的状态处于 rejected 时,本函数被调用,
					// 此时将 self 对象的 PromiseStatus 变量的值改为 rejected ,
					// self 的 PromiseValue 变量的值改为 rejectedResult				
					amendPromiseRejected.bind(self,rejectedResult)();
				}
			);
		}else if (resolvedValue.then !== undefined) {
			// 如果 resolvedValue 参数的值是一个包含有then方法的对象,
			// 那也可以使用then方法作为修改 self 对象的 PromiseStatus 和 PromiseValue 变量的值 的参考

			// 被修改的Promise对象
			let self = this;

			// 使用 amendPromiseResolved 方法修改 self 对象的内部变量
			let amendResolve = function(resolvedResult){ 
				amendPromiseResolved.bind(self,resolvedResult)(); 
			};

			// 使用 amendPromiseRejected 方法修改 self 对象的内部变量
			let amendReject = function(rejectedResult){
				amendPromiseRejected.bind(self,rejectedResult)();
			}

			// 这个then方法内部内部由用户自定义的,何时调用 amendResolve 方法,
			// 还是调用 amendReject 方法,由用户自己来决定。
			resolvedValue.then(amendResolve,amendReject);
		}else{
			// 如果 resolvedValue 参数的值不是 Promise对象 时,
			// 直接将 this 这个 Promise对象 的 PromiseStatus 变量的值改为 resolved,
			// this 的 PromiseValue 变量的值改为 resolvedValue 参数的值。
			this['PromiseStatus'] = 'resolved';
			this['PromiseValue'] = resolvedValue;

			// 因为 this 的状态从 pending 变为 resolved,
			// 所以唤醒 this 中记录的 resolvedMicroTasks 数组下的 所有微任务
			callNoteMicroTask(this);
		}
	}
};

/* 
 * 修改 Promise对象 的 PromiseStatus 变量为 rejected 
 * 修改 Promise对象 的 PromiseValue 变量为 方法的参数的值 
 */
let amendPromiseRejected = function(rejectedValue){
	// 只有当 Promise对象 的 状态 是 pending 时才可以修改
	if (this['PromiseStatus']==='pending') {

		// 不管 rejectedValue 参数的数据类型是什么,哪怕是Promise对象。
		// 直接将 this 这个 Promise对象 的 PromiseStatus 变量的值改为 rejected,
		// this 的 PromiseValue 变量的值改为 rejectedValue 参数的值。
		this['PromiseStatus'] = 'rejected';
		this['PromiseValue'] = rejectedValue;

		// 因为 this 的状态从 pending 变为 rejected
		// 所以唤醒 this 中记录的 rejectedMicroTasks 数组下的 所有微任务
		callNoteMicroTask(this);
		
	}
};

/*
 * 唤醒参数 Promise对象上 记录过的微任务
 */
let callNoteMicroTask = function(promise){
	let status = promise['PromiseStatus']
	let microTaskArr;

	swtich(status){
		case 'pending':
			break;
		case 'resolved':
			// 获取参数 Promise对象 在 resolved 状态下的所有微任务
			microTaskArr = promise.note.resolvedMicroTasks;
			break;
		case 'rejected':
			// 获取参数 Promise对象 在 rejected 状态下的所有微任务
			microTaskArr = promise.note.rejectedMicroTasks;
			break;
	}

	if(microTaskArr !== null && microTaskArr !== undefined && microTaskArr.length > 0){
		/*
		 * 将 microTaskArr 数组中的所有微任务执行代码按顺序一个个加入到微任务队列中。
		 * 加入微任务队列中的微任务会从记录中移除
	 	 */
	}
}

class Promise{

	constructor(paraFun){
		
		// Promise构造函数调用时,必须传递函数对象作为参数。
		if (Object.prototype.toString.call(paraFun).indexOf('Function')!=-1) {
			
			// 新建Promise对象 的 PromiseStatus 和 PromiseValue 变量的值分别是 pending 和 undefined
			this['PromiseStatus'] = 'pending';
			this['PromiseValue'] = undefined;

			// 当 Promise对象 处于 pending 状态时调用 then 方法时,会用到这个记录对象
			// 记录 所有 形成的微任务。
			this.note = {
				resolvedMicroTasks:[],
				rejectedMicroTasks:[]
			}


			try{
				// 调用构造函数传入的参数函数
				// 将 amendPromiseResolved 和 新建Promise对象 绑定生成一个 属于 新建Promise对象 的函数,作为参数一
				// 将 amendPromiseRejected 和 新建Promise对象 绑定生成一个 属于 新建Promise对象 的函数,作为参数二
				paraFun(
					amendPromiseResolved.bind(this),
					amendPromiseRejected.bind(this)
				);
			}catch(error){
				// 如果在paraFun方法在调用时抛出错误时
				// 直接修改 新建Promise对象 的 PromiseStatus 变量的值为 rejected 
				// PromiseValue 变量的值为 抛出的错误
				amendPromiseRejected.bind(this,error)();
			}

		}else{
			throw new Error('调用Promise构造函数时必须使用函数对象作为参数');
		}
	}

	then(callback1,callback2){
		// 要素一:
		let self = this; // 调用 then 方法的 Promise对象

		// 要素二:
		let amendResolve; // 用于修改 新建Promise对象 为 resolved 状态的函数
		let amendReject; // 用于修改 新建Promise对象 为 rejected 状态的函数
		// 新建一个 Promise对象,其会作为 then 方法的返回值
		let p = new Promise((RESOLVE,REJECT) => {
			// 将可以修改 新建Promise对象 内部变量 的两个函数导出构造函数,下面有用
			amendResolve = RESOLVE;
			amendReject = REJECT;
		});

		// 要素三:
		let resolvedCallBack = callback1; // 当 self 对象的状态为 resolved 时会被间接调用的函数
		let rejectedCallBack = callback2; // 当 self 对象的状态为 rejected 时会被间接调用的函数

		// 两种微任务
		let resolvedMicroTask; // 当 self 对象的状态为 resolved 时,根据不同情况修改 新建Promise对象 的 内部变量
		let rejectedMicroTask; // 当 self 对象的状态为 rejected 时,根据不同情况修改 新建Promise对象 的 内部变量

		if (resolvedCallBack &&
			Object.prototype.toString.call(resolvedCallBack).indexOf('Function')!=-1) {
			// 当 then 方法的参数一存在,且是一个函数对象,
			// 那就把 resolvedCallBack 函数的返回值,作为修改 新建Promise对象 内部变量 的参考。
			resolvedMicroTask = function(){
				// 本函数对象的 执行代码 代表了一个 微任务
				
				// 本函数对象会先添加到 self 对象下的记录微任务中的对应 resolved 状态的数组中
				
				// 当 self 对象的状态 为 resolved 时,会从记录数组中取出 本函数对象,
				// 将函数对象的 执行代码 作为一个 微任务 加入到 微任务队列中

				// 这个微任务的目的是以 resolvedCallBack 函数的调用作为参考,去修改 新建Promise对象 内部变量
				try{
					// resolvedCallBack 函数在调用时,会以 self 的 PromiseValue 变量的值 作为参数
					var result = resolvedCallBack(self['PromiseValue']); 
					// 调用 amendResolve 函数修改 新建Promise对象 的 PromiseStatus 变量的值为 resolved
					// 修改 PromiseValue 变量的值以 resolvedCallBack 函数的返回值作为参考
					// 即使 result 的数据类型是Promise,但是 amendResolve 函数内部会对其进行特殊处理。
					amendResolve(result);
				}catch(error){
					// 如果 resolvedCallBack 函数在调用时,出现 错误抛出 ,
					// 修改 新建Promise对象 的 PromiseStatus 变量的值为 rejected
					// PromiseValue 变量的值为 抛出的错误
					amendReject(error);
				}	
			}	
		}else{
			// 当 then 方法被调用时,参数一不是一个函数对象,
			// 那就把 self 对像的 内部变量 作为修改 新建Promise对象 内部变量 的参考
			resolvedMicroTask = function(){
				// 本函数对象的 执行代码 代表了一个 微任务
				
				// 本函数对象会先添加到 self 对象下的记录微任务中的对应 resolved 状态的数组中
				
				// 当 self 对象的状态 为 resolved 时,会从记录数组中取出 本函数对象,
				// 将函数对象的 执行代码 作为一个 微任务 加入到 微任务队列中

				// 这个微任务的目的是以 self 对像的 内部变量 作为参考,去修改 新建Promise对象 内部变量
				if (self['PromiseStatus'] === 'resolved') {
					// self 对象的状态是 resolved 时,
					// 修改 新建Promise对象 的 PromiseStatus 变量的值为 resolved
					// PromiseValue 变量的值为 self 对象的 PromiseValue 变量的值
					amendResolve(self['PromisValue']);
				}else if (self['PromiseStatus'] === 'rejected') {
					// self 对象的状态是 rejected 时,
					// 修改 新建Promise对象 的 PromiseStatus 变量的值为 rejected
					// PromiseValue 变量的值为 self 对象的 PromiseValue 变量的值
					amendReject(self['PromisValue']);
				}
			}
		}

		if (rejectedCallBack &&
			Object.prototype.toString.call(rejectedCallBack).indexOf('Function')!=-1) {
			// 当 then 方法的参数二存在,且是一个函数对象,
			// 那就把 rejectedCallBack 函数的返回值,作为修改 新建Promise对象 内部变量 的参考。
			rejectedMicroTask = function(){
				// 本函数对象的 执行代码 代表了一个 微任务
				
				// 本函数对象会先添加到 self 对象下的记录微任务中的对应 rejected 状态的数组中
				
				// 当 self 对象的状态 为 rejected 时,会从记录数组中取出 本函数对象,
				// 将函数对象的 执行代码 作为一个 微任务 加入到 微任务队列中

				// 这个微任务的目的是以 rejectedCallBack 函数的调用作为参考,去修改 新建Promise对象 内部变量
				try{
					// rejectedCallBack 函数在调用时,会以 self 的 PromiseValue 变量的值 作为参数
					var result = rejectedCallBack(self['PromiseValue']);
					// 调用 amendResolve 函数修改 新建Promise对象 的 PromiseStatus 变量的值为 resolved
					// 修改 PromiseValue 变量的值以 rejectedCallBack 函数的返回值作为参考
					// 即使 result 的数据类型是Promise,但是 amendResolve 函数内部会对其进行特殊处理。
					amendResolve(result); 
					// 虽然 rejectedMicroTask 函数是在状态为 rejected 时被调用,但是这里依然要使用 amendResolve 函数修改内部变量		
				}catch(error){
					// 如果 rejectedCallBack 函数在调用时,出现 错误抛出 ,
					// 修改 新建Promise对象 的 PromiseStatus 变量的值为 rejected
					// PromiseValue 变量的值为 抛出的错误
					amendReject(error);
				}
			}
		}else{
			// 当 then 方法被调用时,参数二不是一个函数对象,
			// 那就把 self 对像的 内部变量 作为修改 新建Promise对象 内部变量 的参考
			rejectedMicroTask = function(){
				// 本函数对象的 执行代码 代表了一个 微任务
				
				// 本函数对象会先添加到 self 对象下的记录微任务中的对应 rejected 状态的数组中
				
				// 当 self 对象的状态 为 rejected 时,会从记录数组中取出 本函数对象,
				// 将函数对象的 执行代码 作为一个 微任务 加入到 微任务队列中

				// 这个微任务的目的是以 self 对像的 内部变量 作为参考,去修改 新建Promise对象 内部变量
				if (self['PromiseStatus'] === 'resolved') {
					// self 对象的状态是 resolved 时,
					// 修改 新建Promise对象 的 PromiseStatus 变量的值为 resolved
					// PromiseValue 变量的值为 self 对象的 PromiseValue 变量的值
					amendResolve(self['PromisValue']);
				}else if (self['PromiseStatus'] === 'rejected') {
					// self 对象的状态是 rejected 时,
					// 修改 新建Promise对象 的 PromiseStatus 变量的值为 rejected
					// PromiseValue 变量的值为 self 对象的 PromiseValue 变量的值
					amendReject(self['PromisValue']);
				}
			}
		}

		// 将分别代表了两种状态下的微任务的 resolvedMicroTask 和 rejectedMicroTask 函数记录到 self 对象上
		self.note.resolvedMicroTasks[resolvedMicroTasks.length] = resolvedMicroTask;
		self.note.rejectedMicroTasks[rejectedMicroTasks.length] = rejectedMicroTask;

		if (self['PromiseStatus'] !== 'pending') {
			// 如果 self 对象在调用 then 方法时,不处于 pending 状态,
			// 那就唤醒 self 中记录的 所有微任务,其实就是将刚形成的微任务直接加入微任务队列中。 
			callNoteMicroTask(self);
		}

		// 此时的 新建实例对象p 的内部变量都是初始值。
		return p;	
	}

	catch(callBack){
		return this.then(null,callBack);
	}

	finally(callBack){
		return this.then(
			(resolvedResult)=>{ callBack(); return resolvedResult; },
			(rejectResult)=>{ callBack(); throw rejectResult; }
		);

		// 这是另一种实现方式,目的是为了既实现效果,也要用箭头函数实现?
		// let P = this.constructor;
  // 		return this.then(
  //   		value  => P.resolve(callback()).then(() => value),
  //   		reason => P.resolve( callback()).then(() => { throw reason })
  // 		);
	}

	static resolve(resolvedValue){
		return new this((RESOLVE,REJECT)=>{
			RESOLVE(resolvedValue);
		});
	}

	static reject(rejectedValue){
		return new this((RESOLVE,REJECT)=>{
			REJECT(rejectedValue);
		});
	}

	static race(arrPromise){
		// race静态方法会返回一个 新建Promise对象
		// 将可以修改 新建Promise对象 的内部变量的函数引导出来
		let resolvP;
	    let rejectP;
	    let p = new Promise((RESOLVE,REJECT)=>{
	        resolvP=RESOLVE;
	        rejectP=REJECT;
	    });

	    for (let arrp of arrPromise){
	    	let promise = Promise.resolve(arrp);

	    	// 给每个Promise对象都设置回调函数,哪个回调先被调用
	    	// 就先用这个回调的状态和返回值去修改 新建Promise对象 的内部变量。
	        promise.then((resolveValue)=>{
	        	resolvP(resolveValue);
	        });
	        promise.catch((rejectValue)=>{
	            rejectP(rejectValue);
	        });
	    }

		return p;	    
	}

	static all(arrPromise){

		// all静态方法会返回一个 新建Promise对象
		// 将可以修改 新建Promise对象 的内部变量的函数引导出来
		let resolvP;
	    let rejectP;
	    let p = new Promise((RESOLVE,REJECT)=>{
	        resolvP=RESOLVE;
	        rejectP=REJECT;
	    });

	    var pValueArr = [];
	    let index = -1;

	    for (let arrp of arrPromise){
	    	let promise = Promise.resolve(arrp);
			let pValueArrIndex = ++index;
	        

	        promise.then((resolveValue)=>{
	        	// 将每个状态为 resolved 的 Promise对象 的 PromiseValue 变量的值进行存储
	            pValueArr[pValueArrIndex] = resolveValue;
	            if(pValueArr.length === arrPromise.length){
	            	// 当 pValueArr 存储的 PromiseValue 变量的值的数量等于 Promise对象 的数量时,
	            	// 修改 新建Promise对象 的内部变量
	        		// 修改 PromiseStatus 变量的值为 resolved
	        		// 修改 PromiseValue 变量的值为 pValueArr
	                resolvP(pValueArr);
	            };
	        });

	        // 只有一有 Promise对象 的状态变为 rejected 时,就修改 新建Promise对象 的内部变量
	        promise.catch((rejectValue)=>{
	        	// 修改 PromiseStatus 变量的值为 rejected
	        	// 修改 PromiseValue 变量的值为 rejectValue
	            rejectP(rejectValue);
	        });
	    }

	    return p;
	}
}

缺点

  1. 当我们在进行异步编程的时候,开启异步的代码一般都写在构造函数的参数函数中,参数函数是随着Promise对象的创建而执行,所以我们没有办法去终止异步的开启。
  2. Promise类中,在构造函数的参数函数以及then方法的参数函数中抛出的错误,我们在Promise外是无法获取到的。
  3. 构造函数的参数函数的函数体中的代码在执行时,我们没法获取具体执行到哪一步了,直到执行RESOLVE函数或者REJECT函数。

参考博客:

  1. http://es6.ruanyifeng.com/#docs/promise
  2. https://www.cnblogs.com/cangqinglang/p/8967268.html
  3. https://www.cnblogs.com/dong-xu/p/7000163.html
  4. https://segmentfault.com/a/1190000014940904
  5. https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/?utm_source=html5weekly
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值