关于我们

质量为本、客户为根、勇于拼搏、务实创新

< 返回新闻公共列表

深入理解Promise

发布时间:2023-06-29 23:00:19
Promise的前提概念 Promise是一个构造函数,用来生成Promise实例 Promise构造函数接受一个函数作为参数,该函数有两个参数,分别是resolve和reject resolve:成功时的回调 reject:失败时的回调 Promise分别有三个状态 1、pending :进行中 2、fulfilled:已成功 3、rejected:已失败 Promise的执行 Promise一旦新建后就会立马执行,无法中途取消,Promise返回的只有两种状态,并且该状态产生就无法更改。也就是说Promise的状态只有以下两种情况: 1、从pending到fulfilled 2、从pending到rejected pending的状态一旦改变,就不会再发生变化。 Promise的结果返回 Promise内部结果一旦返回,resolve或reject其中的任何一种,即可产生回调。 1、resolve的回调在then()中获取(成功的回调) 2、reject的回调在cathc()中获取(失败,捕获错误) let promise = new Promise(function(resolve,reject){ // sth... let i = 5; if( i > 1){ resolve(i) // 成功 } else { reject(error) // 失败 } }) promise.then(el=>{ // el即为resolve的参数 console.log(el); // 5 }) .catch(err=>{ console.log(err); }) Promise的执行顺序 Promise创建后会立马执行,then()是一个异步回调,将在所有的同步任务执行完后执行then(),也就是说:Promise在新建的时候会立即执行,then()或catch()为异步任务,当所有同步任务执行完后才会执行then() let pro = new Promise(function(resolve,reject){ console.log('Promise新建'); resolve() }) pro.then(el=>{ console.log('.then回调'); }) console.log('同步任务'); 上述代码的执行结果: 1、Promise新建 2、同步任务 3、then()回调 Promise参数和执行顺序 resolve函数的参数出了可以携带正常值以外,还可以携带Promise实例 let pro = new Promise(function(resolve,reject){ let i = 10 if(i < 50){ resolve(i) } else { reject(error) } }) let pro2 = new Promise(function(resolve,reject){ resolve(pro) // 参数为上一个Promise实例 }) pro2.then(el=>{ console.log(el); // 10 }) .catch(err=>{ console.log(err); }) 执行顺序: 先执行pro,如果pro的状态是pending时,pro2则会等待执行,当pro的状态变成resolve或rejected时,pro2则会立马执行,由于pro决定pro2的状态,所以后面的then变成了针对pro Promise的嵌套执行 let p = new Promise((resolve,reject)=>{ let i = 10 if(i > 5){ resolve(i) } else { reject('pro错误') } }) let p2 = new Promise((resolve,reject)=>{ p.then(el=>{ console.log(el * 2); // 20 if(el > 6){ // 此处需要写成return resolve(el)看起来会更正常 resolve(el) console.log('我是p1的.then回调'); // 我是p1的.then回调 } else { reject('pro2错误') } }) }) let p3 = new Promise((resolve,reject)=>{ p2.then(el=>{ if(el > 50){ resolve(el) } else { reject('pro3错误') } }) }) 多个Promise的执行顺序: 新建Promise后会立马执行,先执行Promise内的任务,执行完成后再执行then() 如果Promise内有其它Promise的then()任务执行(这里简称p2),会先将p2的then()任务执行完毕,再执行自身的then()任务 多个Promise并行,会先按顺序执行Promise的任务,再分别按照顺序分别执行他们的then()任务 需要注意的是:上面p2的Promse中,正常来说,resolve或reject执行完后,Promise的使命应该完成,但此处因为执行顺序的问题,并没有先执行resolve,而是执行了console的任务,因该写成return resolve(value)的形式,让Promise更正常一些,否则会出现不必要的意外。 Promise.prototype.then() then方法是定义在原型对象Promise.prototype上的,它可以为Promise实例添加状态改变时的回调函数。 then有两个参数(可选): resolved:成功状态的回调函数 rejected:失败状态的回调函数 then返回的是一个新的Promise实例,因此可以采用链式写法,then()后面还可以接then() let p = new Promise((resolve,reject)=>{ let i = 5; if(i > 4){ resolve(i) } }) p.then(el=>{ console.log('then1:'+ el); if(el > 3){ return el } }).then(el=>{ console.log('then2:'+ el * 2); }).catch(err=>{ console.log(err); } /* 输出结果: then1:5 then2:10 */ 上面代码使用then方法,第一个then()回调函数执行完后,会将返回结果传入第二个回调函数,也就是说,第二个then()的参数是第一个then()返回的结果。 catch的捕获具有冒泡性质,也就是说,前面不论.then了多少次,里面发生的错误,都会被最后一个catch捕获到。 Promise.prototype.catch() Promise.prototype.catch()方法是.then(null,rejection)的别名,也就是说,catch其实是用来指定发生错误时的回调函数。 let p = new Promise((resolve,reject)=>{ reject('错误信息') }) p.then(el=>{ // sth... }).catch(err=>{ console.log(err); // 错误信息 }) 上述代码中,p方法返回Promise对象,该对象返回的是reject,then()方法调用回调函数,then()有两个参数,一个resolved和rejected,如果Promise返回的是reject,那么.then的状态就会变成rejected,rejected会调用catch()方法指定的回调函数,处理该错误,catch的参数就是reject抛出的错误信息。 如果then运行中抛出错误,也会被catch()捕获 let p2 = new Promise((resolve,reject)=>{ resolve(100) }) p2.then(el=>{ throw new Error('then抛错') }).catch(err=>{ console.log(err); // Error: then抛错 }) catch捕捉错误的方式 // 方式一 let promise = new Promise(function(resolve,reject){ try { throw new Error('方式一错误') } catch(e){ reject(e) } }) promise.catch(err=>{ console.log(err); // 方式一错误 }) // 方式二 let promise2 = new Promise(function(resolve,reject){ reject(new Error('方式二错误')) }) promise2.catch(err=>{ console.log(err); // 方式二错误 }) // 方式三 let promise3 = new Promise((resolve,reject)=>{ reject('方式三错误') }) promise3.catch(err=>{ console.log(err); // 方式三错误 }) 以上三种效果是一样的,前两种通过Error抛出异常,后一种直接抛出异常 catch返回的也是一个Promise对象,后面还可以接着调用then()方法 如果Promise的状态变成resolved,那么再抛出错误是无效的,因为Promise的状态一旦发生改变,就会永久保持该状态。 执行阻塞问题 Promise内部错误不会影响到到异步队列中的Promise外部代码,同步代码还是会造成阻塞 let p3 = new Promise((resolve,reject)=>{ resolve(str) // ReferenceError: str is not defined }) p3.then(el=>{console.log(el)}) setTimeout(()=>{console.log('Promise外部代码')},1000) // Promise外部代码 console.log('同步代码'); // 未打印 Promise在建立时会立即执行,此处报错了,所以后面的同步代码都无法执行,但是异步的setTimeout还是会执行,并不受影响。 finally() finally有最后、终于的意思,也就是说finally()方法不管Promise最后的状态如何,都会执行 let p = new Promise((resolve,reject)=>{ resolve('东方不败') }) p.then(el=>{ console.log(el); // 东方不败 }) .catch(err=>{ console.log(err); }) .finally(()=> console.log('finally执行') // finally执行 ) 上述代码中,不管Promise最后的状态怎么样,finally都会执行,即使没有then()和catch(),finally也会执行 需要注意的是,finally不接受任何参数,但是finally的执行顺序跟内部是否是一个函数有关,如果finally内部是一个函数,那么finally会在Promise最后执行,如果finally内部没有任何函数,直接输出,那么finally会在Promise最前面执行。 finally的执行跟Promise的状态无关。 Promise.all() Promise.all()方法用于将多个Promise实例包装成一个新的Promise实例 语法:let p = Promise.all([p1,p2,p3]) let p = new Promise((resolve,reject)=>{ resolve(10) }) p.then(el=>{ console.log(el); }) let p2 = new Promise((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:2 当Promise.all的两个promise参数状态都变成fulfilled时,才会触发Promise.all()的方法,如果其中以一个参数被rejected,那么Promise.all()也会变成rejected的状态,会被catch捕捉。 上面代码中,如果p2没有chach方法,则会调用Promise.all()的catch race() Promise.race()方法可以将多个Promise实例包装成一个新的Promise实例。 语法: let p = Promise.race([p1,p2,p3]) let p = new Promise((resolve,reject)=>{ setTimeout(()=>{ resolve('p1状态') },2000) }) let p2 = new Promise((resolve,reject)=>{ setTimeout(()=>{ resolve('p2状态') },1000) }) let p3 = new Promise((resolve,reject)=>{ setTimeout(()=>{ resolve('p3状态') },3000) }) let p4 = new Promise((resolve,reject)=>{ setTimeout(()=>{ reject('reject:p4状态') },4000) }) let promise = Promise.race([p,p2,p3,p4]) promise .then(el=>{ console.log(el); // p2状态 }) .catch(err=>{ console.log(err); }) race有竞争,角逐的意思,该方法其实就是参数中,哪一个Promise的状态先发生改变,就调用先发生改变的promise,这个el就是最先发生改变的promise的返回值。 Promise.allSettled() Promise.allSettled()方法可以在所有异步操作都结束的情况下,调用该方法。它与Promise.all()方法不同的是,all()方法需要所有异步都成功才会进入then(),而allSettled()不需要。 let p = new Promise((resolve,reject)=>{ resolve('p1状态') }) let p2 = new Promise((resolve,reject)=>{ setTimeout(()=>{ reject('p2状态') },3000) }) Promise.allSettled([p,p2]) .then(el=> console.log(el) ) Promise.allSettled()的参数不管是reject还是resolve都会触发,只要参数的状态全都发生改变即可,上面代码中,p2延迟了3s状态才发生改变,那么Promise.allSettled()则会等待参数的状态全都发生改变时触发。 Promise.any() Promise.any()方法接受一组Promise实例作为参数,包装成一个新的Promise实例返回。 let p = new Promise((resolve,reject)=>{ resolve('p1状态') }) let p2 = new Promise((resolve,reject)=>{ reject('p2状态') }) Promise.any([p,p2]) .then(el=>{ console.log('then:',el); // then: p1状态 }) .catch(err=>{ console.log('catch:',err); }) 只要有一个参数的状态变成fulfilled,Promise.any就会变成fulfilled状态 如果所有的参数实例都变成rejected状态,包装实例才会变成rejected状态 也就是说,any是只要有一个参数成功,any就可以then,所有参数都reject,any才会catch Promise.resolve() Promise.resolve()可以将现有对象转为Promise对象,Promise.resolve()方法就起到这个作用。 语法:let p = Promise.resolve(obj) let p = Promise.resolve('obj') p.then(el=> console.log(el)) // obj // 其实相当于 let p2 = new Promise(resolve=>resolve('obj')) 如果Promise.resolve()没有参数,那么将直接返回resolved状态的Promise对象,并且是一个没有参数的then()方法。 let p3 = Promise.resolve() p3.then(el=>{ console.log(el); // undefined }) setTimeout(()=>{ console.log('定时器'); },0) Promise.resolve() .then(()=>{ setTimeout(()=>{console.log('resolve内定时器')},0) console.log('resolve内部'); }) console.log('外部'); Promise的执行顺序,Promise是一个异步的微任务,同步任务执行完后才会执行异步任务,异步的微任务会先执行,微任务执行完后才会执行异步的宏任务,此处的setTimeout是异步的宏任务,所以会最后执行,外部的定时器是异步的宏任务,Promise内部的定时器是异步微任务内的宏任务,所以微任务内的宏任务会更慢执行,此处的执行顺序为:外部、resolve内部、定时器、resolve内定时器。 Promise.reject() Promise.reject()返回一个新的Promise实例,该实例的状态为rejected let p = Promise.reject('错误信息') p.catch(err=>console.log(err)) // 等同于 let p2 = new Promise((resolve,reject)=>{ reject('错误信息2') }) p2.catch(err=>console.log(err)) Promise.try() 实际开发中,会遇到这么一种情况,不管函数是同步还是异步的,都想用Promise来处理它,因为这样就可以用then()指定下一步流程,catch处理抛出的错误,更便于管理流程。 基于Promise我们可以用async做到同步的代码风格执行异步操作,现在有一个最新的提案,使用Promise.try()来捕获所有的同步和异步错误。 为什么这么做? Promise抛出的错误,catch可以捕获到,但此处仅限于当前Promise的异常错误,也就是异步的。如果同步错误抛出,那么catch()是捕捉不到的,需要在外层再嵌套一层try...catch,这样写起来非常麻烦,代码如下: try { let p = new Promise((resolve,reject)=>{ reject('异步错误') }) p.catch(err=> void console.log(err) ) // 异步错误 throw Error('同步错误') } catch (e) { console.log(e); // 同步错误 } // 这样catch两次,可以处理同步和异步的抛错问题 如果使用Promise.try()就可以捕获所有的同步和异步错误 Promise.try(()=>{ let p2 = new Promise((resolve,reject)=>{ reject('异步错误2') }) throw Error('同步错误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哟

/template/Home/leiyu/PC/Static