自学前端Promise的理解和应用,好程序员分享Promise模式实现
您是一名在古老迷失城市中探险的冒险家。您身处一间装饰华丽的房间中,四周布满了古老的壁画和雕塑。您发现有两个通道分别通向不同的方向,分别是:一个黑暗的通道和一个明亮的通道。
黑暗的通道中充满了神秘的气息,您能感受到其中蕴含的古老力量。您并不知道它会带您去哪里,但您可以感受到其中蕴藏的危险和未知。另一方面,明亮的通道看起来温暖舒适,您可以看到远处照耀的阳光。您能听到一些欢快的音乐声,似乎在那个方向上有一些人在欢庆。
现在,您需要在这两个通道之间做出选择。您会选择哪一个通道呢?您的选择将会影响您接下来的冒险之旅。
先来了解几个单词,几个Promise的术语
● Promise的三个状态:pending(等待状态)、fulfilled(已成功状态)和 rejected(已失败状态)
● Promise的三个常用回调函数(咱说的是常用):
1. 实例化对象时传入的回调函数
2. then方法的传入的两个回调函数
3. catch方法传入的一个回调函数
以作业为例,继续深入了解Promise
1、开始写作业,此时Promise状态为pending,我们可以理解为初始状态,也可以理解为业务处理中
2、作业完成情况有两种,一种是写完了,一种是被狗吃了
3、无论哪种情况,必须要告诉老师,成功了就通过resolve通道暂存数据,同时会更改状态为成功fulfilled;失败了就通过reject通道暂存数据,同时修改状态为rejected。
4、老师如何获取resolve或reject通道传来的数据呢?可以使用Promise对象的.then方法获取resolve或reject的结果,或者使用.catch方法获取reject通道的信息。
5、简单来一段代码看看
const p = new Promise(function (resolve, reject) { resolve('通过成功通道存储数据')})//...如果不使用then,提取,则数据一直暂存在p对象中//提取结果p.then(function (val) { console.log(val)})
6、这些写法都是固定的,建议使用promise之前一定要充分了解回调函数
7、咱们再升级一下,写作业用了5秒钟
const p1 = new Promise(function (resolve, reject) { //写作业用了5秒钟,5秒钟之后,把成功的作业暂存到resolve通道中 setTimeout(() => { resolve('这是作业') }, 5000)})//上面代码中调用了resolve的时候,then里面的回调函数才会触发,这里有个时间差或者时空差的感觉p1.then(function (val) { console.log(val)})
8、再升级一下,这作业他也有被狗吃了的时候,我们假定5秒钟之外就被狗吃了,5秒钟之内就是完成的
const p2 = new Promise(function (resolve, reject) { //生成一个1~6秒之间的随机数 const time = Math.random() * 4000 + 2000 setTimeout(() => { if (time <= 5000) { resolve('成功交作业啦') } else { reject(`作业被狗吃了,耗费了${time秒}`) } }, time)})//成功使用then来接,失败了怎么拿到参数呢?//用then的第二个参数来拿失败的参数p2.then(function (val) { console.log(val)}, function (err) { console.log('估计是被狗吃了', err)})
9、除了then的第二个参数可以拿到失败的结果,还可以通过catch方法拿到结果,一会再讨论这两种用法的区别,先看catch的用法,注意这里需要连用
p2.then(function (val) { console.log(val)}).catch((reason) => { //输出失败原因,大概率是被狗吃了 console.log('估计是被狗吃了', reason)})
10、再看一种常用的连用的写法
new Promise(function (resolve, reject) { //生成一个1~6秒之间的随机数 const time = Math.random() * 4000 + 2000 setTimeout(() => { if (time <= 5000) { resolve('成功交作业啦') } else { reject(`作业被狗吃了,耗费了${time}秒`) } }, time)}).then(function (val) { console.log(val)}).catch((reason) => { //输出失败原因,大概率是被狗吃了 console.log('估计是被狗吃了', reason)})
11、一些需注意的地方
1. resolve和reject只是一个形参的名字,对应实际的值是promise内部的函数,调用这两个其实调用的就是promise内部的某个函数而已,这个名字可以随便去改,例如
new Promise(function (ok, fail) { //此时此刻形参叫ok,但实际代表的是promise内部函数 ok('ojbk')}).then((res) => { //promise内部会存储数据,传给res这个变量,此时res 值就是ojbk console.log(res)})
2. new Promise(构造器的参数是一个函数),这个函数会同步执行,代码执行到这里的时候就会立即执行
12、小结
1. Promise通过构造函数同步执行,执行结果调用promise的内部函数存储,通常叫resolve和reject,一个是成功时存储存储使用的通道,另一个是失败时存储的通道,无论存储到哪个通道中,都是写代码的人去定义的
2. then有两个参数,分别是两个函数类型的参数,第一个参数是在调用resolve时会触发,第二个参数是在调用reject时触发
3. catch方法可以替代then的第二个参数,拿到reject结果
13、附赠then第二个参数和catch的区别
在then的第一个参数里面的代码,如果出现异常的时候,不用手动的try...catch,通过promise实例对象的.catch可以捕获then内出现的异常,但注意,catch不会捕获构造函数代码中的错误,来看例子
new Promise(function (ok, fail) { setTimeout(() => { //故意5秒后触发k的报错 console.log(k) }, 5000)}).then((res) => { console.log(res)}).catch(error => { //这个时候,error是拿不到那个错误的,他不负责console.log(k)所在代码块中出现的错误 console.log(error)})
再看一个catch方法能捕获什么地方的错误
大概就是这么个大概
使用 Promise 的主要原因是他可以解决回调地狱(回调嵌套)问题,让代码变得更优雅,逻辑更清晰
举一个生活中的例子,早上起床第一件事是要穿拖鞋,第二件事是洗漱,第三件事是穿衣服,第四件事是查看“身手要钱”,第五件事是打开自家房门出去开车上班,每件事都需要串行,每一步与下一步都有关联关系
function foo() { //1、穿拖鞋开始 setTimeout(() => { //1、2秒后穿拖鞋完成 //2、洗漱开始 setTimeout(() => { //2、2秒后洗漱完成 //3、穿衣服开始 setTimeout(()=>{ //3、穿衣服完成 //....不好意思看官,后边还有好几个步骤咱就意思一下,再写就吐了 },2) }, 2) }, 2)}foo()
就写这几层吧,是不是太恶心了
new Promise((resolve, reject) => { //1、穿拖鞋 setTimeout(() => { resolve('穿拖鞋搞定') }, 2000)}).then(val => { //等待穿拖鞋完成后,会调用这个函数 //2、洗漱 //注意此处!!!,必须使用return 返回一个新的promise来完成链式调用 const p = new Promise((resolve, reject) => { setTimeout(() => { resolve('洗漱搞定') }, 2000) }) return p}).then(val => { //3、穿衣服,此处直接返回,没有使用中间变量 return new Promise((resolve, reject) => { setTimeout(() => { resolve('穿衣服搞定') }, 2000) })}).then(val => { //4、查看“身手要钱” return new Promise((resolve, reject) => { setTimeout(() => { resolve('查看“身手要钱”搞定') }, 2000) })}).then(val => { //5、开车去上班 // 元气满满的一天})
就图这~
那么多方法,不讲那么多,race、all什么的网上一抓一大把
先了解一个基础规则
● await必须修饰的是Promise对象
● await必须在async中使用
● await只能接收resolve通道的结果,reject结果会导致报错
● await修饰的代码在执行时,代码是卡住的,类似于alert,这句代码不执行完,后边的代码不会向下执行,这也类似于线程同步执行的概念,这也是await有用之处
● async修饰的必须是函数
● async修饰后的函数在执行之后会转为Promise对象
看一段简单的代码
async function foo() { let k = await new Promise(function (resolve, reject) { setTimeout(() => { resolve('qfedu') }, 2000) }) console.log(k)}foo()
这样用倒是更麻烦,我们把要处理的事黏黏糊糊多弄一些试一试
async function foo() { let level1 = await new Promise((resolve, reject) => { //1、穿拖鞋 setTimeout(() => { resolve('穿拖鞋搞定') }, 1000) }) //拿着第一步的结果,去第二步进行操作 let level2 = await new Promise((resolve, reject) => { setTimeout(() => { resolve(level1 + '洗漱搞定') }, 1000) }) let level3 = await new Promise((resolve, reject) => { setTimeout(() => { resolve(level2 + '穿衣服搞定') }, 1000) }) let level4 = await new Promise((resolve, reject) => { setTimeout(() => { resolve(level3 + '查看“身手要钱”搞定') }, 1000) }) console.log(level4 + ',之后开车去上班,元气满满的一天')}foo()
输出结果:
这样代码看起来更加简洁,当然要重点考虑的问题是在整个从上到下的调用过程中,任何一个环节出现问题,都会影响下面的代码
再来,我们把代码聚焦到foo()方法调用之前和调用之后
console.log(1)foo() //这个会输出 穿拖鞋搞定洗漱搞定穿衣服搞定查看“身手要钱”搞定......等console.log(2)
思考一下,程序输出的顺序
注意,使用async包裹的代码,属于异步代码,会在同步代码之后执行
我们给按钮添加一个点击事件,看点击按钮如何让程序使用await顺序执行
async function foo() { let level1 = await new Promise((resolve, reject) => { //1、穿拖鞋 setTimeout(() => { resolve('穿拖鞋搞定') }, 1000) }) //拿着第一步的结果,去第二步进行操作 let level2 = await new Promise((resolve, reject) => { setTimeout(() => { resolve(level1 + '洗漱搞定') }, 1000) }) let level3 = await new Promise((resolve, reject) => { setTimeout(() => { resolve(level2 + '穿衣服搞定') }, 1000) }) let level4 = await new Promise((resolve, reject) => { setTimeout(() => { resolve(level3 + '查看“身手要钱”搞定') }, 1000) }) console.log(level4 + ',之后开车去qfedu上班,元气满满的一天')}window.onclick = foo;//或者是window.onclick = async function(){ //todo ... //await new Promise... //await new Promise... //await new Promise... //...}
实际场景中,await 与async通常用来处理ajax请求类代码,让代码更简洁,再次强调,await只接收resolve结果,注意reject和error的错误要使用try...catch...处理异常。
今日的好程序员的分享就到这里,想了解更多,欢迎评论区探讨,或者dd小源~
举报/反馈
最近更新素质教育
- 信用信息赋能,助力社会信用体系建设
- 今天距第二届全国博士后创新创业大赛总决赛还有10天!
- 北京自学考试和成教有什么不同?
- 道县:开展2023年基础教育业务工作培训
- 融入智能技术 赋能创新人才培养
- 怎么知道自己是不是脊柱侧弯?一个方法教你判断!
- 让长三角市民共享,上海城市业余联赛市面越做越大
- 华容县实验小学:最美莫过夕阳红 最浓不过敬老情
- 原创恭喜!王楚钦代替樊振东世界排名来到第一位,双方积分差仅有15分
- 自考会计专业有哪些课程?
- 播出预告丨上海市天山初级中学:不一样的学习评价
- 杭州亚残运会火炬传递圆满成功 “桂冠”画出现代版“富春山居图”
- 自考学士学位英语什么时候报名
- 日本学校要求的托福分数
- 证明差“老师”能教出好“学生”?江苏博士最新研究成果,引发国际关注!
- 让共同发展繁荣之路越走越宽广
- 原创拒绝恩比德!尼克斯目标清晰!锡伯杜点名要他,三分王成球队答案
- 喜庆乔迁五周年,共度九九重阳节
- 蚂蚁庄园今日答案最新:味精加热太久真的会致癌吗
- 2024年辽宁省普通高考报名即将开始
- 粤港澳大湾区第三届职业技能大赛将于10月23
- 难忘那次5天5夜的长途机动,还有那个同车的义务兵驾驶员
- “周五课堂”来了新老师
- 在学生中走俏的萝卜刀引发争议,消保委提醒:警惕其暴力暗示
- 校园圈丨让学生走出家门做历史作业,结果出人意料