其实很早就想写Promise的使用了。一是在实际编码过程中经常用到,二是有时候朋友在使用的时候会遇到一些问题。Promises确实是ES6中的API特性之一,它对你编写JS的方式影响最大。本文是实际使用过程的总结。查看文件创建时间2017-10-09,拖延症真是要命。..还是需要加大执行力!不忘初心,加油!博客原站前言&&基本概念Promise是一个JS异步的解决方案。与传统的回调函数相比,Promise可以解决多个回调嵌套严重的问题。一个Promise对象代表一个异步操作,具有三种状态:pending、fulfilled或rejected。状态转换只能是pending->fulfilled或者pending->rejected,这个过程一旦发生,就不可逆了。个人认为讲解Promise其实需要分两部分来讲解Promise构造函数的使用。Promise原型对象上的一些方法。PromiseconstructorES6规定Promise对象是用于生成Promise实例的构造函数。Promise构造函数接受一个函数作为参数,该函数的两个参数是resolve和reject。它们是两个函数,由JavaScript引擎提供,你不需要自己部署。resolve函数的作用是将Promise对象的状态从“未完成”变为“成功”(即从pending变为fulfilled),在异步操作成功时调用,将异步操作的结果作为一个参数;reject函数作用是将Promise对象的状态从“未完成”变为“失败”(即从pending变为rejected),在异步操作失败时调用,将异步操作上报的错误作为一个范围。以下代码创建一个Promise实例。functionrequest(){returnnewPromise((resolve,reject)=>{/*异步操作成功*/setTimeout(()=>{resolve("success");},1000);//取消注释可以在这里体现,Promise的状态一旦改变,就不会再改变//reject('error');});}接收request().then(result=>{console.info(result);}).catch(错误=>{console.info(error);});在上面的newPromise()之后,除了使用catch来捕捉错误之外,还可以使用then方法指定resolve和reject的回调函数来达到捕捉错误的目的。request().then(result=>{console.info(result);},error=>{console.info(error);});原型上的方法Promise.prototype.then()p.then(onFulfilled,onRejected);then方法是定义在Promise.prototype上的方法。和上面的例子一样,它有两个参数,fulfilled回调函数和rejected回调函数,第二个参数是可选的。两个关键点:then方法的返回值是一个新的Promise实例,所以对于调用者来说,在得到一个Promise对象后,调用then后仍然返回一个Promise,其行为与回调函数的返回相同在然后值相关。如下:如果then中的回调函数有返回值,那么then返回的Promise就会变成接受状态,返回值会作为接受状态的回调函数的参数值。如果then中的回调函数抛出错误,则then返回的Promise会变成rejected,抛出的错误会作为rejected状态回调函数的参数值。如果then中的回调函数返回的是一个已经处于接受状态的Promise,那么then返回的Promise也会变成接受状态,该Promise的接受状态的回调函数的参数值将作为接受状态返回的Promise参数值的接受状态回调函数。如果then中的回调函数返回的是一个已经处于rejected状态的Promise,那么then返回的Promise也会变成rejected,将那个Promise的rejected状态的回调函数的参数值作为rejected状态返回的Promise参数值的回调函数。如果then中的回调函数返回的是一个pending的Promise,那么then返回的Promise的状态也是pending,其最终状态与那个Promise的最终状态相同;同时,变为final时调用的回调函数参数与Promise变为finalized时回调函数的参数相同。连锁电话。将嵌套回调的代码格式转化为垂直模式的链式调用。例如回调形式:回调地狱的例子a(a1=>{b(a1,b1=>{c(b1,c1=>{d(c1,d1=>{console.log(d1);});});});});这样的水平扩展可以修改为(a,b,c,d)是返回Promisea().then(b).then(c).then(d)的函数。then(d1=>{console.log(d1);});//=====可能上面的例子不太好看===下面的更直观a().then(a1=>b(a1)).then(b1=>c(b1)).then(c1=>d(c1)).then(d1=>{console.log(d1);});这样的垂直结构看起来干净多了。Promise.prototype.catch()除了then()之外,Promise.prototype原型链上还有一个catch()方法,它是一个rejection的handler。事实上,它的行为与调用Promise.prototype.then(undefined,onRejected)相同。(其实调用obj.catch(onRejected)内部会调用obj.then(undefined,onRejected))。//1.request().then(result=>{console.info(result);},error=>{console.info(error);});//2.request().then(result=>{console.info(result);}).catch(error=>{console.info(error);});如上例:使用了两种方法,结果基本等同,但还是推荐第二种方法。下面我会给出原因:Promise链中的Promise.prototype.then(undefined,onRejected),onRejected方法无法捕捉到当前Promise抛出的错误,后续的.catch可以捕捉到之前的错误。代码冗余newPromise((resolve,reject)=>{setTimeout(()=>{resolve("reject");},1000);}).then(result=>{console.log(result+"1");throwError(result+"1");//抛出一个错误},error=>{console.log(error+":1");//这里不会出现}).then(result=>{console.log(result+"2");returnPromise.resolve(result+"2");},error=>{console.log(error+":2");});//reject1,错误:reject1:2如果使用.catch方法,代码会简化很多,实际上是扩展了Promise链newPromise((resolve,reject)=>{setTimeout(()=>{resolve("reject");},1000);}).then(result=>{console.log(result+"1");throwError(result+"1");//抛出错误}).then(result=>{console.log(result+"2");returnPromise.resolve(result+"2");}).catch(err=>{console.log(err);});//reject1,Error:reject1:2Promise.prototype.finally()还没有完全成为标准的一部分,它在:Stage4finally()方法返回一个Promise,执行完then()和catch()之后会执行finally指定的回调函数(回调函数中没有参数,只表示Promise结束,相当于用.then+.catch扩展了原来的效果Promise链,避免了相同的语句需要()和catch()各写一次,mdn-Promise-finallyPromise对象上的Promise.all()方法用于处理并发的Promise,Promise的all会封装多个Promise实例进入一个新的Promise实例,新的promise的状态取决于多个Promise实例的状态,只有当所有的Promise都被fulfilled时,新的instance才会被fulfilled,如果参数中的一个Promise失败(被拒绝),这个instance会回调Failed(rejecte),失败原因是第一个失败的Promise的结果。举个例子:Promise.all([newPromise(resolve=>{setTimeout(resolve,1000,"p1");}),newPromise(resolve=>{setTimeout(resolve,2000,"p2");}),newPromise(resolve=>{setTimeout(resolve,3000,"p3");})]).then(result=>{console.info("then",result);}).catch(error=>{console.info("catch",error);});//[p1,p2,p3]Promise.all([newPromise(resolve=>{setTimeout(resolve,1000,"p1");}),newPromise(resolve=>{setTimeout(resolve,2000,"p2");}),Promise.reject("p3error")]).then(result=>{console.info("then",result);}).catch(error=>{console.info("catch",error);});//p3错误获取cnode社区的高级贴的前十条内容fetch("https://cnodejs.org/api/v1/topics?tab=good&limit=10").then(res=>res.json()).then(res=>{constfetchList=res.data.map(item=>{returnfetch(`https://cnodejs.org/api/v1/topic/${item.id}`).then(res=>res.json()).then(res=>res.data);});Promise.all(fetchList).then(list=>{console.log(list);});});Promise.race()竞速执行Promise。race也是将多个Promise实例封装成一个新的Promise实例,但是新Promise的状态取决于先改变状态的Promise实例的状态。前端最典型的用法之一就是模拟fetchapi的请求超时。Promise.race([fetch("https://cnodejs.org/api/v1/topics?tab=good&limit=10").then(res=>res.json()),newPromise((解决,拒绝)=>{setTimeout(reject,1,"error");})]).then(result=>{console.info("then",result);}).catch(error=>{console.info("catch",error);//在此处输入});在上面的例子中,只要请求在1毫秒内没有结束,就会进入.catch()方法。虽然无法取消请求,但超时模拟成功。Promise.resolve(value)&&Promise.reject(reason)这两个方法都可以用来创建和返回一个新的Promise,不同的是Promise.resolve(value)携带进新的Promise状态是fulfilled。Promise.reject(reason)带来的rejected有时可以用来简化创建Promise的一些操作,比如:constsleep=(time=0)=>newPromise(resolve=>setTimeout(resolve,time));//在这里创建一个睡眠,并打印Promise.resolve()的链.then(()=>{console.log(1);}).then(()=>sleep(1000)).then(()=>{console.log(2);}).then(()=>sleep(2000)).then(()=>{console.log(3);});有时也用于手动更改Promise链,当然,这其实和直接返回一个值没有区别,或者使用throwError来构造错误。如何使用看个人喜好newPromise((resolve,reject)=>{setTimeout(()=>{resolve("resolve");//1.},1000);}).then(result=>{returnPromise.reject("reject1");//2.}).then(result=>{returnPromise.resolve(result+"2");},err=>{returnPromise.resolve(err);//3.}).then(res=>{console.log(res);//4.}).catch(err=>{console.log(err+"err");});//Areject1的几个例子先看几个例子:关于执行顺序,可以搜索详情,jsloopnewPromise((resolve,reject)=>{console.log("step1");resolve();console.log("step2");}).then(()=>{console.log("step3");});console.log("step4");//步骤一,步骤二,step4,Step3使用Promise构造函数构造Promise时,回调函数中的内容会立即执行,而Promise.then中的函数是异步执行的。关于状态不可变让我们开始吧;constp=newPromise((resolve,reject)=>{setTimeout(()=>{start=Date.now();console.log("once");resolve("success");},1000);});p.then(res=>{console.log(res,Date.now()-开始);});p.then(res=>{console.log(res,Date.now()-开始);});p.then(res=>{console.log(res,Date.now()-开始);});Promise构造函数只执行一次,一旦内部状态发生变化,有了一个值,无论调用多少次then(),都只会得到那个结果。好像可以改变状态constp1=newPromise((resolve,reject)=>{setTimeout(()=>{resolve("success");},1000);});constp2=p1.then((resolve,reject)=>{thrownewError("error");});console.log("p1",p1);console.log("p2",p2);setTimeout(()=>{console.log("p1",p1);console.log("p2",p2);},2000);观察第一次打印两个Promise时,本次打印是pending,因为p2是基于p1的结果,p1是pending,马上打印出来肯定是pending;第二次打印的时候,因为p1的状态是resolved,p2是rejected,这不是fulfilled的状态变成了rejected,而是p2是一个新的Promise实例,then()返回一个新的承诺实例。关于透传Promise.resolve(11).then(1).then(2).then(3).then(res=>{console.info("res",res);});//11forthen方法传递一个非函数值,相当于then(null),会造成穿透效果,即直接通过then(),直到then()符合规范。Promise的串行调用使用Array.reduce方法执行Promiseconstsleep=(time=0)=>newPromise(resolve=>setTimeout(resolve,time));[1000,2000,3000,4000].reduce((Promise,item,index)=>{returnPromise.then(res=>{console.log(index+1);returnsleep(item);});},Promise.resolve());//在各自的等待时间后输出1、2、3、4这篇文章到这里基本就结束了,相信大家如果能看懂上面的内容并运用到实际项目中。它应该使工作更有效率,并且对于新的异步使用来说应该更得心应手。Promise的使用比较简单,关于如何实现一个Promise可能会有另一篇高质量的文章。那些收集的承诺。bluebird是一个扩展了Promise方法的库,提供了很多实用的方法。推荐[[思维导图]Promise-《你不知道的 JavaScript》-Volume2-Part2](https://zhuanlan.zhihu.com/p/...[[翻译]ASimpleES6PromiseGuide](https://zhuanlan.zhihu.com/p/.../zhuanlan.zhihu.com/p/...阮一峰-ES6入门Promise对象Promise不够中性WHY“PROMISESARENETNEUTRALENOUGH”ISNOTNEUTRALENOUGH【翻译】Promises必须知道的9个TipsAboutPromises(十问))EventLoop必知(六问)
