node.js学习笔记之promise
这篇文章说说我对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"); }});//结果接收方式1promise.then(function (res){ console.log(res);},function (err){ console.log(err);});//结果接收方式2promise.then(function (res){ console.log(res);}).catch(function (err){ console.log(err);});//结果接收方式3promise.then(function (res){ console.log(res);});promise.catch(function (err){ console.log(err);});
常规用法就是声明一个promise对象,写入逻辑,根据逻辑的返回结果确定是执行成功还是执行失败,执行成功就调用resolve方法,该方法接受一个参数,可以把逻辑返回的结果传到外面来使用。执行失败可以调用reject方法,该方法也可以传数据到外部,一般是传错误信息。
在上面的代码中我写了三种结果接收方式,先说第一种:
//结果接收方式1promise.then(function (res){ console.log(res);},function (err){ console.log(err);});
promise有then方法,可以传两个参数,两个参数都是function,用来接收数据,第一个参数是接收逻辑执行成功的返回值,第二个参数接收逻辑执行失败的返回值。当然这种看上去也会有一些嵌套的感觉,我一般是不用这种写法的。
//结果接收方式2promise.then(function (res){ console.log(res);}).catch(function (err){ console.log(err);});
我的个人理解是promise提供了then方法,又提供了catch方法,就是为了分别接收不同的逻辑执行结果的,then方法就是为了接收成功返回的结果,那相应的,catch方法就是为了接收失败返回的结果,node.js中链式调用很常见,这种写法也是一种链式调用,我是比较喜欢这种用法的。
//结果接收方式3promise.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方法,但我试验了却报错了,有兴趣的同学可以研究一下。