前言
谈到Promise,我们都知道它有一个pending属性,该属性有两种状态:成功(resolved/fulfilled)、失败(rejected),而且一个Promise只可能从“等待”转到“失败”或者“成功”;且状态一旦改变便是不可逆的,谈到Promise,很多人想到的就是解决回调地狱的问题,当我们在需要多个异步请求都执行完再执行下一步操作时,可以考虑使用promise。以下是个人学习Promise记录的知识点
一、Promise的理解和使用
1.Promise是什么?
1.1.1 理解
- 抽象表达
Promise 是一门新的技术(ES6 规范)
Promise 是 JS 中进行异步编程的新解决方案
备注:旧方案是单纯使用回调函数 - Promise 的状态改变
pending 变为 resolved
pending 变为 rejected
1.1.2 Promise的状态改变
- 具体表达
说明: 只 2 种, 且一个 Promise 对象只能改变一次,无论变为成功还是失败, 都会有一个结果数据, 成功的结果数据一般称为 Value, 失败的结果数据一般称为 reason
1.1.3 Promise 的基本流程
1.1.4 Promise 的基本使用
- 代演示
constbut=document.getElementById("but") but.addEventListener("click",function (){ letrandomNumber=Math.ceil(Math.random()*100) //这里new Promiseconstp=newPromise(function (resolve, reject){ setTimeout(()=>{ if(randomNumber<30){ //成功的状态resolve("中奖了!") }else{ //失败的的状态reject("抱歉,很遗憾!") } },1000) }) p.then(value=> { //这里是p的pending属性为resolve/fulfilledalert(value) },reason=> { //这里是p的pending属性为rejectedalert(reason) }) })
2.为什么要用 Promise?
1.2.1 指定回调函数的方式更加灵活
- 旧的: 必须在启动异步任务前指定
- Promise: 启动异步任务 => 返回Promise对象 => 给Promise对象绑定回调函数(甚至可以任务结束后指定多个回调)
1.2.2 支持链式调用, 可以解决回调地狱问题
- 什么是回调地狱?
- 回调函数嵌套调用, 外部回调函数异步执行的结果是嵌套的回调执行的条件
- 回调地狱的缺点?
- 不便于阅读
- 不便于异常处理
- 解终极解决方案?
- Promise链式调用
3.如何使用 Promise?
1.3.1 API
1.3.1.1 不属于new Promise的API(4个)
Promise.prototype.then方法:(onResolved(成功), onRejected(失败)) => {}
- resolve
letp1=Promise.resolve(521); letp2=Promise.resolve( newPromise((resolve, reject) => { // resolve('OK');reject("Error"); }) ); // 如果不对出错进行处理(指定错误回调),控制台就会报错p2.then((value) => { console.log(value); },(reason) => { console.log(reason); }); console.log(p1) console.log(p2) resolve的参数问题:(以失败的状态为例)
1.如果传入的参数为 非Promise类型的对象, 则返回的结果为成功Promise对象
2.如果传入的参数为 Promise 对象, 则Promise对象的状态决定了 p2执行onResolve还是OnRejected
2.reject
letp1=Promise.reject(521); letp2=Promise.reject('haha'); letp3=Promise.reject( newPromise((resolve, reject) => { resolve("OK"); }) ); p3.then((value) => { console.log(value); },(reason) => { console.log(reason); }); console.log(p1) console.log(p2) console.log(p3);
reject的参数问题:
1.如果传入的参数为 非Promise类型的对象, 则new Promise返回的结果永远为失败Promise对象
2.如果传入的参数为 Promise 对象, 则Promise对象的状态决定了 p执行onResolve还是OnRejected
3.all
letp1=newPromise((resolve, reject) => { resolve("OK"); }); letp2=Promise.resolve('Success'); // let p2 = Promise.reject("Error");letp3=Promise.resolve("Oh Yeah"); constresult=Promise.all([p1, p2, p3]); console.log(result);
all方法:(这里以成功为例)
说明: Promise.all(这里是一个数组,数组里面有一个一个的Promise),他返回一个新的 Promise, 只有所有的 Promise都成功才成功, 只要有一个失败了就直接失败。
4.race
letp1=newPromise((resolve, reject) => { setTimeout(() => { reject("OK"); }, 1000); }); letp2=Promise.resolve("Error"); letp3=Promise.resolve("Oh Yeah"); //调用constresult=Promise.race([p1, p2, p3]); console.log(result);
race方法:(这里以成功为例)
说明: 返回一个新的 Promise, 第一个完成的 Promise的结果状态就是最终的结果状
1.3.1.2 属于new Promise的API
1.catch
letp=newPromise((resolve, reject) => { reject('error'); }); p.catch(reason=> { console.log(reason); });
catch方法:
说明: 处理错误的方法
1.3.2 Promise 的几个关键问题
1.3.2.1 如何改变 Promise 的状态?
- 改变 Promise 的状态的几种方法:代码演示
letp=newPromise((resolve, reject) => { //1. resolve 函数// resolve("ok"); // pending => fulfilled (resolved)//2. reject 函数reject("error"); // pending => rejected//3. 抛出错误// throw "出问题了";});
1.3.2.2 一个 Promise 指定多个成功/失败回调函数, 都会调用吗?
- 当 Promise改变为对应状态时都会调用
- 代码演示
letp=newPromise((resolve, reject) => { resolve("OK"); //如果注释掉resolve(),那么p的状态就还是pending,即状态未发生改变,不会调用then}); ///指定回调 - 1p.then((value) => { console.log(value); }); //指定回调 - 2p.then((value) => { console.log(value); });
1.3.2.3 改变 Promise 状态和指定回调函数谁先谁后?
都有可能, 正常情况下是先指定回调再改变状态, 但也可以先改状态再指定回调
如何先改状态再指定回调?
在执行器中直接调用 resolve()/reject()
延迟更长时间才调用 then()
什么时候才能得到数据?
如果先指定的回调, 那当状态发生改变时, 回调函数就会调用, 得到数据
如果先改变的状态, 那当指定回调时, 回调函数就会调用, 得到数据
代码验证演示
letstartTime=Date.now() console.log("开始记时",startTime) letp=newPromise((resolve, reject) => { setTimeout(() => { resolve("OK"); letendTime=Date.now()-startTimeconsole.log("结束记时",endTime) }, 1000); }); p.then( (value) => { console.log(value); }, (reason) => {} );
1.3.2.4 Promise .then()返回的新 Promise 的结果状态由什么决定?
- Promise .then()返回的新 Promise 的结果状态由什么决定?
- 代码验证演示
letp=newPromise((resolve, reject) => { resolve("ok"); // reject("no");}); //执行 then 方法letresult=p.then( (value) => { // console.log(value);//1. 抛出错误 rejected// throw "出了问题";//2. 返回结果是非 Promise 类型的对象 fulfilled// return 521;//3. 返回结果是 Promise 对象 fulfilled/rejectedreturnnewPromise((resolve, reject) => { // resolve("success");reject("error"); }); }, (reason) => { console.warn(reason); } ) console.log(result); // 这里result返回的依旧是一个Promise,以下是对Promise的成功或失败的处理result.then((value)=>{ console.log(value) },(reason)=>{ console.log(reason) })
说明: 代码的执行流程
举例:如果开始执行resolve(),那么执行then的onResolved方法
如果onResolved方法返回非Promise对象,那么Promise .then()返回的新Promise的状态就为成功!
如果onResolved方法为Promise对象,那么该Promise返回的状态(成功或者失败/resolve或reject)就决定了Promise .then()返回的新Promise的状态。
1.3.2.5 Promise 如何串连多个操作任务?
- Promise的 then()返回一个新的 Promise, 可以开成 then()的链式调用
- 通过 then 的链式调用串连多个同步/异步任务
- 代码验证演示
letp=newPromise((resolve, reject) => { setTimeout(() => { resolve('OK'); }, 1000); }); p.then(value=> { returnnewPromise((resolve, reject) => { resolve("success"); }); }).then(value=> { console.log(value); }).then(value=> { //因为上个then没有返回任何东西所以输出undefinedconsole.log(value); })
1.3.2.6 Promise 异常传透?
- 当使用 Promise 的 then 链式调用时, 可以在最后指定失败的回调
- 前面任何操作出了异常, 都会传到最后失败的回调中处理
- 代码验证演示
letp=newPromise((resolve, reject) => { setTimeout(() => { resolve('OK'); // reject('Err'); }, 1000); }); p.then(value=> { // console.log(111);throw'失败啦!'; }).then(value=> { console.log(222); }).then(value=> { console.log(333); }).catch(reason=> { //用then也可以捕获异常,不过then要传两个参数console.warn(reason); }); console.log(p)
1.3.2.7 如何中断 Promise 链?
- 当使用 Promise 的 then 链式调用时, 在中间中断, 不再调用后面的回调函数
- 办法: 在回调函数中返回一个 pendding 状态的 Promise 对象
- 代码验证演示
letp=newPromise((resolve, reject) => { setTimeout(() => { resolve('OK'); }, 1000); }); leta=p.then(value=> { console.log(111); //有且只有一个方式returnnewPromise(() => { }); }).then(value=> { console.log(222); }).then(value=> { console.log(333); }).catch(reason=> { console.warn(reason); });
1.3.3 PromiseE8 之 async 和 await
1.3.1 mdn 文档
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements/async_function
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/await
1.3.3 async 函数
- 使用async该函数的返回值为 Promise对象
- Promise对象的结果由 async 函数执行的返回值决定
asyncfunctionmain() { //1. 如果返回值是一个非Promise类型的数据 则状态为成功,返回一个成功的promise对象return521; } asyncfunctionmain1() { //2. 如果返回的是一个Promise对象 则状态取决于返回结果returnnewPromise((resolve, reject) => { // resolve('OK'); //成功reject("Error"); //失败 }); } asyncfunctionmain2() { // 3. 抛出异常 返回一个失败的promise对象throw"Oh NO"; } letresult=main(); letresult1=main1(); letresult3=main2(); console.log(result); console.log(result1); console.log(result3);
1.3.4 await 表达式
- await 右侧的表达式一般为 Promise对象, 但也可以是其它的值
- 如果表达式是 Promise对象, await 返回的是 Promise成功的值
- 如果表达式是其它值, 直接将此值作为 await 的返回值
1.3.5 注意
- await 必须写在 async 函数中, 但 async 函数中可以没有 await
- 如果 await 的 Promise失败了, 就会抛出异常, 需要通过 try…catch 捕获处理
asyncfunctionmain() { letp=newPromise((resolve, reject) => { resolve("OK"); // reject("Error"); }); //1. 右侧为promise的情况// let res = await p;//2. 右侧为其他类型的数据// let res2 = await 20;//3. 如果promise是失败的状态try { letres3=awaitp; console.log(res3) } catch (e) { //catch捕获失败状态console.log(e); } }
总结
觉得有帮助的,记得给个赞👍
以上就是个人学习Promise的相关知识点,如有错漏之处,敬请指正。