这篇文章说说我对promise的理解。

promise在ES6之前就有的写法,在ES6中写入了语言标准,于是就有了原生promise对象。

promise对象能更好的改善异步操作的回调地狱,把多层嵌套扁平化,看上去像同步执行的代码,更容易阅读和理解。由于js语法的灵活多变,也导致了promise的写法多样。

promise有三种状态来表示当前执行的进度,pending,resolve,reject。promise执行后,默认是pending状态,意思是正在执行,promise有两种状态变化,并且是不可逆的,第一种就是从pending到resolve,从正在执行到执行成功,第二种是pending到reject,从执行中到执行失败。

promise一旦开始执行,就不能停止,直到执行结束。

如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。

当处于Pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。

下面我就针对promise的常规用法,链式调用,all方法,race方法这四方面来说说我对promise的理解。



先说常规用法:

let promise = new Promise(function (resolve,reject){
	let res = 1+2+3;
	if(res>1){
		resolve(res);
	}else{
		reject("err");
	}
});
//结果接收方式1
promise.then(function (res){
	console.log(res);
},function (err){
	console.log(err);
});
//结果接收方式2
promise.then(function (res){
	console.log(res);
}).catch(function (err){
	console.log(err);
});
//结果接收方式3
promise.then(function (res){
	console.log(res);
});
promise.catch(function (err){
	console.log(err);
});

常规用法就是声明一个promise对象,写入逻辑,根据逻辑的返回结果确定是执行成功还是执行失败,执行成功就调用resolve方法,该方法接受一个参数,可以把逻辑返回的结果传到外面来使用。执行失败可以调用reject方法,该方法也可以传数据到外部,一般是传错误信息。

在上面的代码中我写了三种结果接收方式,先说第一种:

//结果接收方式1
promise.then(function (res){
	console.log(res);
},function (err){
	console.log(err);
});

promise有then方法,可以传两个参数,两个参数都是function,用来接收数据,第一个参数是接收逻辑执行成功的返回值,第二个参数接收逻辑执行失败的返回值。当然这种看上去也会有一些嵌套的感觉,我一般是不用这种写法的。

//结果接收方式2
promise.then(function (res){
	console.log(res);
}).catch(function (err){
	console.log(err);
});

我的个人理解是promise提供了then方法,又提供了catch方法,就是为了分别接收不同的逻辑执行结果的,then方法就是为了接收成功返回的结果,那相应的,catch方法就是为了接收失败返回的结果,node.js中链式调用很常见,这种写法也是一种链式调用,我是比较喜欢这种用法的。

//结果接收方式3
promise.then(function (res){
	console.log(res);
});
promise.catch(function (err){
	console.log(err);
});

当然,既然promise提供了then和catch两个方法接收结果,自然也可以用方法3来接收逻辑返回的结果,有些童鞋喜欢看起来很明朗的代码风格,那就是这种了,这种只是比第二种方式多了(promise对象.)这么一点代码。其实看起来是挺工整的。


下面再说说promise的链式调用:

假设一种情况:你在A文件里记录的B文件的名字,在B文件里记录的C的名字,又在C文件里记录的D文件的名字,你现在知道A文件的名字,想得到D文件的内容,该怎么来写呢?我会这样写:

new Promise(function(resolve,reject){
	let res1 = 1;//res1逻辑
	if(res1){
		console.log("res1",res1);
		resolve(res1);
	}else{
		reject('new Error1()');
	}
}).then(function (res){
	return new Promise(function (resolve,reject){
		let res2 = 2+res;//res2逻辑
		if(res2){
			console.log("res2",res2);
			resolve(res2);
		}else{
			reject("new Error2");
		}
	});
}).then(function (res){
	return new Promise(function (resolve,reject){
		let res3 = 3 + res;//res3逻辑
		if(res3){
			console.log("res3",res3);
			resolve(res3);
		}else{
			reject('new Error3');
		}
	});		
}).then(function (res){
			console.log("res",res);//res3结果
		}
);

先在res1逻辑中用A文件的名字读取A文件的内容,得到的结果resolve出去。在then中接收。

然后在res2逻辑中取得A文件内容,解析出B文件名字,再读取B文件内容,resolve出去。在then中接收,一直到res3结果这里得到D文件的内容,如果是用回调函数的话估计就要嵌套很多层了,而用promise,就能很直观的看清代码走势,是不是很简单,当然这一段代码我一直觉得应该有更简洁的写法,但无奈本领不到家,只能写成这样了,如果有大神看见的话,请赐教。

这一段就是promise的链式调用,写个简洁的就是

new Promise().then().then().then();


好吧,下面说说promise的all方法:

function test(value){
	let promise = new Promise(function (resolve,reject){
		value = value * 2;
		if(value){
			resolve(value);
		}else{
			reject("err"+value);
		}
	});
	return promise;
}

let promArr = [1,2,3,4,5,6,7,8,9,10].map(function (i){
	return test(i);
});
Promise.all(promArr).then(function (posts){
	console.log(posts);
}).catch(function (err){
	console.log(err); 
});

这里假设我需要执行10个异步操作,并把他们的结果放到一个数组里同时传给一个方法,那all方法就能派上用场了,在这里需要重点说明一下:如果10个promise都是返回成功的话,也就是promise内部逻辑都是调用了resolve(value)方法的话,promise.then才能接收到最终的10个promise的结果组成的数组,就是上面代码中的posts,假如其中一个promise执行失败,那么,不好意思,你就只能在catch中收到这个失败的promise返回的错误信息了,是的,只能收到执行失败的promise返回的错误信息,这就是all方法。大概可以理解成这样:

let a = true && true && true && true;

当所有的表达式都为true时a才能等于true,有一个表达式为false时,a就不能等于true;


下面再说一下race方法。这个方法有点奇特,举个例子,一个孕妇怀了四胞胎,那谁是老大呢,当然是先出生的是老大了,而race方法最奇特的地方就在于我只想知道老大是谁,不管后面谁是老二老三,或者说race是一个非常狠心的父亲,只想要老大这一个孩子,后面的小孩一个也不要,就算出生了也是扔在医院不管不问。看代码:

function test(value){
	let promise = new Promise(function (resolve,reject){
		setTimeout(function (){
			resolve(value);
		},Math.random() * 10000);
	});
	return promise;
}

let promArr = [1,2,3,4,5,6,7,8,9,10].map(function (i){
	return test(i);
});
Promise.race(promArr).then(function (post){
	console.log('post',post);
}).catch(function (err){
	console.log('err',err); 
}).finally(function (){
	console.log('finally');
});

这个例子的意思是有10个promise,每个的逻辑都是延时一段时间,时间随机,谁先执行完谁就先返回。

最后的结果是post只能得到一个值,但别的延时还没执行完之前,这段程序不会结束,那也就意味着其他9个promise仍然会执行到底,但我们是获取不到他们9个的结果的,只能得到第一个返回的promise的结果。

恩。就先暂时说这么多吧,promise还有一个done方法和finally方法,done方法是放在then链最后,是用来捕获中间发生的任何异常的,这个没有试验,finally据说是不论then和catch执行了哪一个都会执行finally方法,但我试验了却报错了,有兴趣的同学可以研究一下。