Promise对象中then的先后顺序

单个Promise对象的then执行顺序其实很容易理解,反正对着then的顺序排列,就能得到then方法的执行顺序。但是在多个异步任务或者说是多个Promise对象组成的代码中,就好像不是这么一回事了。

老规矩,说出程序运行结果:

new Promise(function(resolve){
	resolve();
}).then(function(){
	console.log(1);
}).then(function(){
	console.log(2);
}).then(function(){
	console.log(3);
})
new Promise(function(resolve){
	resolve();
}).then(function(){
	console.log(4);
}).then(function(){
	console.log(5);
}).then(function(){
	console.log(6);
})

乍一看,没难度,不就是123456吗?但是正确答案如下:

1
4
2
5
3
6

先说一个结论,then方法进入异步队列的时间跟所属Promise对象中resolve()执行的时间点有关,较早resolve()的Promise对象后续then方法则早进入任务队列,当线程空闲的时候则优先执行。

另外一个结论,同一个Promise对象下的then方法并不是拖家带口进入异步队列,而是同一个Promise对象每次只进一个then方法,当这个then方法完成之后同一Promise对象下的后续一个then方法才得以进入。

接下来继续看题:

new Promise(function(resolve){
	setTimeout(function(){
		resolve();
	},0);
}).then(function(){
	console.log(1);
}).then(function(){
	console.log(2);
}).then(function(){
	console.log(3);
})
new Promise(function(resolve){
	resolve();
}).then(function(){
	console.log(4);
}).then(function(){
	console.log(5);
}).then(function(){
	console.log(6);
})

正确答案为:

4
5
6
1
2
3

如果你的答案是:142536,那你要明白setTimeout方法是异步方法,在同步代码执行完之后,setTimeout才开始计时,计时到了才会执行回调函数内的代码resolve()。

如果你的答案是:415263,那你要明白setTimeout是属于异步任务里面的宏任务,而且异步任务也是分先后顺序的,大概是先微任务再宏任务,Promise.then和Promise.catch属于微任务

再看题:

new Promise(function(resolve){
	setTimeout(function(){
		resolve();
	},100);
}).then(function(){
	console.log(1);
}).then(function(){
	console.log(2);
}).then(function(){
	console.log(3);
})
new Promise(function(resolve){
	setTimeout(function(){
		resolve();
	},0);
}).then(function(){
	console.log(4);
}).then(function(){
	console.log(5);
}).then(function(){
	console.log(6);
})

正确的结果如下:

4
5
6
1
2
3

这次你答对了吗?

另外setTimeout的时间如果小于1ms往往会由于硬件不同得出不同的结果,例如刚刚那题如果两个setTimeout分别为0ms和1ms(两个时间值先后顺序调换测试),则setTimeout时延似乎不对resolve的先后顺序造成影响。当然本文写于2020年,如果未来的那一天浏览器引擎对此进行了优化,那这个0ms和1ms的结论也有可能会被推翻,请以自己的测试为准,当然也欢迎补充

发表评论

电子邮件地址不会被公开。 必填项已用*标注