当前位置: 首页 > Web前端 > vue.js

深入理解Promise

时间:2023-03-31 16:27:36 vue.js

Promise的前提概念。Promise是用于生成Promise实例的构造函数。Promise构造函数接受一个函数作为参数。该函数有两个参数,解析和拒绝。resolve:成功时回调reject:fail回调Promise有三种状态:1.Pending:进行中。2.成就:成功。而且这个状态是无法改变的。也就是说,Promise的状态只有以下两种情况:1.从pending到fulfilled2.pending到rejectedpending的状态一旦改变,就不会再改变。Promise的结果返回Promise的内部结果。一旦Promise的内部结果返回,resolve或reject中的任何一个都可以产生回调。1、在then()中获取resolve的回调(成功回调)2、在cathc()中获取reject的回调(失败,捕获错误)letpromise=newPromise(function(resolve,reject){//sth...leti=5;if(i>1){resolve(i)//成功}else{reject(error)//失败}})promise.then(el=>{//el是参数resolveconsole.log(el);//5}).catch(err=>{console.log(err);})Promise的执行顺序Promise创建后会立即执行,then()是异步回调,会在所有Executethen()执行完同步任务之后,也就是说:Promise在新建时会立即执行,then()或catch()是异步任务,then()会被在所有同步任务执行完后执行callback');})console.log('同步任务');上述代码的执行结果:1.新建Promise2.同步任务3.Then()回调Promise参数和执行顺序。resolve函数的参数除了可以携带普通值外,还可以携带Promise实例letpro=newPromise(function(resolve,reject){leti=10if(i<50){resolve(i)}else{reject(error)}})letpro2=newPromise(function(resolve,reject){resolve(pro)//参数为之前的Promise实例})pro2.then(el=>{console.log(el);//10}).catch(err=>{console.log(err);})执行顺序:如果pro的状态为pending,则先执行pro,pro2将等待执行。当pro的状态变为resolved或rejected时,pro2会立即执行。由于pro决定了pro2的状态,下面就变成了proPromise的嵌套执行letp=newPromise((resolve,reject)=>{leti=10if(i>5){resolve(i)}else{reject('proerror')}})letp2=newPromise((resolve,reject)=>{p.then(el=>{console.log(el*2);//20if(el>6){//这里需要写成returnresolve(el)看起来更正常resolve(el)console.log('Iamthe.thencallbackofp1');//我是p1的.thencallback}else{reject('pro2error')}})})letp3=newPromise((resolve,reject)=>{p2.then(el=>{if(el>50){resolve(el)}else{reject('pro3error')}})})多个Promise的执行顺序:新的Promise创建后,会立即执行,先执行promise中的任务,然后执行then()。如果Promise(这里称为p2)中还有其他Promise的then()任务,则p2的then()任务会先执行。然后执行自己的then()任务。多个Promise将并行执行。Promise任务会按顺序执行,然后它们的then()任务会按顺序执行。需要注意的是,在上面p2的Promse中,正常情况下,resolve或者reject执行完后,应该完成Promise的任务,但是这里因为执行顺序的问题,并没有先执行resolve,而是先执行了resolve的任务console是执行的,所以应该写成returnresolve(value)的形式,让Promise更正常一些,否则会出现不必要的意外。Promise.prototype.then()then方法定义在原型对象Promise.prototype上,可以在Promise实例状态变化时添加回调函数。then有两个参数(可选):resolved:成功状态下的回调函数rejected:失败状态下的回调函数then返回一个新的Promise实例,所以可以写成chain,然后then()后面可以接then()letp=newPromise((resolve,reject)=>{leti=5;if(i>4){resolve(i)}})p.then(el=>{console.log('then1:'+el);if(el>3){returnel}}).then(el=>{console.log('then2:'+el*2);}).catch(err=>{console.log(err);}/*Outputresult:then1:5then2:10*/以上代码使用了then方法,第一个then()回调函数执行后,将返回结果传递给第二个回调函数,即也就是说,第二个then()的参数是第一个then()返回的结果,catch的捕获有冒泡的特性,也就是说,无论之前用了多少次.then,报错里面发生的会被最后一个catch捕获的Promise.prototype.catch()Promise.prototype.catch()方法是.then(null,rejection)的别名,也就是说catch实际上是用来指定错误发生时的回调函数。letp=newPromise((resolve,reject)=>{reject('errormessage')})p.then(el=>{//sth...}).catch(err=>{console.log(err);//错误信息})上面代码中,p方法返回一个Promise对象,返回reject,then()方法调用回调函数,then()有两个参数,一个resolved和rejected,ifPromise返回reject,那么.then的状态就会变成rejected,rejected会调用catch()方法指定的回调函数来处理错误,catch的参数就是reject抛出的错误信息。如果then操作中抛出错误,也会被catch()捕获letp2=newPromise((resolve,reject)=>{resolve(100)})p2.then(el=>{thrownewError('thenthrowWrong')}).catch(err=>{console.log(err);//Error:thenthrowsanerror})catch捕获错误的方法//方法一letpromise=newPromise(function(resolve,reject){try{thrownewError('方法1错误')}catch(e){reject(e)}})promise.catch(err=>{console.log(err);//方法1错误})//方法二letpromise2=newPromise(function(resolve,reject){reject(newError('方法2错误'))})promise2.catch(err=>{console.log(err);//方法2error})//方法3letpromise3=newPromise((resolve,reject)=>{reject('方法3error')})promise3.catch(err=>{console.log(err);//方法3error})以上三种效果是一样的。前两种通过Error抛出异常,后者直接抛出异常。catch的返回也是一个Promise对象,后面可以调用then()方法。如果Promise的状态变为resolved,那么Rethrowinganerror是无效的,因为一旦Promise的状态发生变化,它将永远保持在那个状态。执行阻塞问题Promise内部错误不会影响异步队列中的Promise外部代码,同步代码仍然会造成阻塞letp3=newPromise((resolve,reject)=>{resolve(str)//ReferenceError:strisnotdefined})p3.then(el=>{console.log(el)})setTimeout(()=>{console.log('Promise外部代码')},1000)//Promise外部代码console.log('同步代码');//未打印的Promise会在建立时立即执行。这里报错,所以后面的同步代码不能执行,但是异步的setTimeout还是会执行,不受影响。finally()finally有final、final的意思,也就是说不管Promise的final状态如何,finally()方法都会执行。letp=newPromise((resolve,reject)=>{resolve('东方无敌')})p.then(el=>{console.log(el);//东方不败}).catch(err=>{console.log(err);}).finally(()=>console.log('finallyexecute')//finallyexecute)在上面的代码中,finally会执行而不管Promise的最终状态,即使有就是没有then()和catch(),finally才会执行。需要注意的是finally是不接受任何参数的,但是finally的执行顺序跟里面是不是函数有关。如果finally里面是一个函数,那么finally会在Promise的最后执行。如果finally里面没有函数,直接输出,那么finally会在Promise的最前面执行。finally的执行与Promise的状态无关。Promise.all()Promise.all()方法用于将多个Promise实例包装成一个新的Promise实例语法:letp=Promise.all([p1,p2,p3])letp=newPromise((resolve,reject)=>{resolve(10)})p.then(el=>{console.log(el);})letp2=newPromise((resolve,reject)=>{//resolve(20)reject(20)})p2.then(el=>{console.log(el);}).catch(err=>{console.log('p2:',err);})//p和p2都返回resolve触发函数Promise.all([p,p2]).then(el=>{console.log(el);}).catch(err=>{console.log('all:',err);})//all:20当Promise.all的两个promise参数都满足时,就会触发Promise.all()方法。如果其中一个参数被拒绝,那么Promise.all()也会变成被拒绝并被catch捕获。上面代码中,如果p2没有chach方法,就会调用Promise.all()的catchrace()。Promise.race()方法可以将多个Promise实例包装成一个新的Promise实例。语法:letp=Promise.race([p1,p2,p3])letp=newPromise((resolve,reject)=>{setTimeout(()=>{resolve('p1status')},2000)})letp2=newPromise((resolve,reject)=>{setTimeout(()=>{resolve('p2status')},1000)})letp3=newPromise((resolve,reject)=>{setTimeout(()=>{resolve('p3status')},3000)})letp4=newPromise((resolve,reject)=>{setTimeout(()=>{reject('reject:p4status')},4000)})letpromise=Promise.race([p,p2,p3,p4])promise.then(el=>{console.log(el);//p2状态}).catch(err=>{console.log(err);})race有竞争、竞争的意思。这个方法其实就是参数,Promise的状态先变化的,先变化的promise会被调用。这个el是最先改变的承诺。返回值。Promise.allSettled()Promise.allSettled()方法可以在所有异步操作完成时被调用。它与Promise.all()方法的不同之处在于,all()方法在进入then()之前需要所有异步成功,而allSettled()则不需要。让p=newPromise((resolve,reject)=>{resolve('p1status')})让p2=newPromise((resolve,reject)=>{setTimeout(()=>{reject('p2status')},3000)})Promise.allSettled([p,p2]).then(el=>console.log(el))Promise.allSettled()参数无论是reject还是resolve都会触发,只要参数的状态都是变化的。上面代码中,p2的状态在延迟3s后发生变化,然后Promise.allSettled()会等待所有参数的状态发生变化并触发。Promise.any()Promise.any()方法接受一组Promise实例作为参数,并将它们包装成一个新的Promise实例以返回。让p=newPromise((resolve,reject)=>{resolve('p1state')})让p2=newPromise((resolve,reject)=>{reject('p2state')})Promise.any([p,p2]).then(el=>{console.log('then:',el);//then:p1status}).catch(err=>{console.log('catch:',err);})只要一个参数的状态变为fulfilled,Promise.any就会变为fulfilled。如果所有参数实例都被拒绝,则包实例将被拒绝。也就是说,any只要有一个参数成功,any就可以,如果所有参数都reject,any就会catchPromise.resolve()Promise.resolve()可以把一个已经存在的对象转成Promise对象,而Promise.resolve()方法就是这样做的。语法:letp=Promise.resolve(obj)letp=Promise.resolve('obj')p.then(el=>console.log(el))//obj//其实等价于letp2=newPromise(resolve=>resolve('obj'))如果Promise.resolve()没有参数,会直接返回resolved状态的Promise对象,是一个没有参数的then()方法。让p3=Promise.resolve()p3.then(el=>{console.log(el);//undefined})setTimeout(()=>{console.log('timer');},0)Promise。resolve().then(()=>{setTimeout(()=>{console.log('解析内部计时器')},0)console.log('解析内部');})console.log('外部');Promise执行顺序,Promise是一个异步微任务,异步任务先执行同步任务,先执行异步微任务,后执行异步宏任务。这里的setTimeout是异步宏任务,所以会最后执行,外部定时器是异步宏任务,Promise的内部定时器是异步微任务中的宏任务,所以微任务中的宏任务会执行比较慢,这里的执行顺序是:external,resolveinternal,timer,resolveinternaltimer。Promise.reject()Promise.reject()返回状态为rejected的新Promise实例letp=Promise.reject('errormessage')p.catch(err=>console.log(err))//等价于letp2=newPromise((resolve,reject)=>{reject('errormessage2')})p2.catch(err=>console.log(err))Promise.try()在实际开发中,会在这样一个情况,不管函数是同步的还是异步的,我都想用Promise来处理,因为then()可以用来指定流程的下一步,catch处理抛出的错误,更容易管理过程。基于Promise,我们可以使用async实现同步的代码风格来进行异步操作。现在有一个最新提议使用Promise.try()来捕获所有同步和异步错误。你为什么这么做?Promise抛出的错误可以用catch捕获,但这里仅限于当前Promise的异常错误,是异步的。如果抛出同步错误,catch()无法捕获,需要在外层再嵌套一层try...catch,写起来很麻烦。代码如下:try{letp=newPromise((resolve,reject)=>{reject('asynchronouserror')})p.catch(err=>voidconsole.log(err))//异步错误throwError('同步错误')}catch(e){console.log(e);//同步错误}//这个catch两次,可以处理同步和异步错误抛出如果使用Promise.try(),你可以捕获所有的同步和异步错误Promise.try(()=>{letp2=newPromise((resolve,reject)=>{reject('异步错误2')})throwError('同步错误2')}).then(el=>{console.log('then:',el);}).catch(err=>{console.log('catch:',err);})这里的Promise.try相当于try代码块,Promise.catch相当于catch代码块。案例源码:https://gitee.com/wang_fan_w/es6-science-institute如果您觉得本文对您有帮助,请点亮star