前端战五渣学JavaScript——Promise
我是要成为海贼王的男人
悟空已成神,鸣人已成影,待路飞成王之时,便是我青春结束时!
悟空陪布玛找寻龙珠,一路拳打比克、斩弗利萨,生个儿子战沙鲁,最后净化布欧,只因承诺要保护地球。鸣人“有话直说,说到做到,这就是我的忍道”,一句会把佐助带回来的承诺,断臂践行。路飞要凑齐10个船员,成为海贼王,我们相信路飞一定会成王,因为我们相信他的承诺。
我为什么说承诺呢,今天主题不是Promise
吗,因为⬇️
回调地狱 Callback Hell
如果看这篇文章的你是有过项目经验的,应该都遭遇过这惨绝人寰的“回调地狱”。“回调地狱”并不是JS或者编程语言中的一种形式,只是大家把这种编程中遇到的现象、问题预定俗称的调侃成“回调地狱”。因为只要陷进去,就很难出来。并且回调地狱在代码层级上会越陷越深,逻辑看着会非常会乱,如下代码⬇️
// 我们用setTimeout模拟线上发送请求等异步执行的函数
setTimeout(() => {console.log(1);setTimeout(() => {console.log(2);setTimeout(() => {console.log(3);}, 1000)}, 1000)
}, 1000);
复制代码
这是三个回调函数嵌套,延迟一秒后输出1,再过一秒输出2,再过一秒输出3。当然现实项目中,每个函数里面处理的逻辑肯定不仅仅只是输入一个数字这么简单,当我们回调嵌套很多的时候,如果产品提出的一个需求我们需要更改执行顺序,这个时候我们会发现嵌套逻辑复杂到难以简单的更改顺序,严重的只能重新写这段的逻辑代码。并且回调函数让逻辑很不清晰。
后来就有人提出了Promise
概念,这个概念意在让异步代码变得非常干净和直观。
Promise 这就是我的忍道
这个概念并不是ES2015首创的,在ES2015标准发布之前,早已有
Promise/A
和Promise/A+
等概念的出现,ES2015中的Promise
标准便源自于Promise/A+
。Promise
最大的目的在于可以让异步函数变得竟然有序,就如我们需要在浏览器中访问一个JSON座位返回格式的第三方API,在数据下载完成后进行JSON解码,通过Promise
来包装异步流程可以使代码变得非常干净。———————摘自《实战ES2015》
上面最重要的一句就是可以让异步函数变得竟然有序,可能有人会说await
和async
也可以让异步函数同步执行,但是await
操作符本来就是用于等待一个Promise对象的。
我们先来看一下Promise
是怎么解决上面回调地狱这样的难题的⬇️
// 封装一层函数
function timeout() {return new Promise((resolve, reject) => {setTimeout(resolve, 1000)})
}
// 按回调函数的逻辑执行
timeout().then(() => {console.log(1);return timeout()
}).then(() => {console.log(2);return timeout()
}).then(() => {console.log(3);
});
复制代码
我们按照回调函数的逻辑用Promise
重新写了一遍,执行结果一样,我们可以看出来,相比回调函数的层级深入,使用Promise
以后函数的层级明显减少了,逻辑清晰许多。
下面我们来从头开始认识Promise
Promise基础
想要给一个函数赋予Promise
的能力,就要先创建一个Promise
对象,并将其作为函数值返回。Promise
构造函数要求传入一个函数,并带有resovle
和reject
参数。一个成功回调函数,一个失败成功回调函数。下面是Promise
对象的三个状态:
- pending: 初始状态,既不是成功,也不是失败状态。
- fulfilled: 意味着操作成功完成。
- rejected: 意味着操作失败。
三个状态的转换关系是从pending -> fulfilled
或者pending -> rejected
,并且状态改变以后就不会再变了。pending -> fulfilled
以后会去执行传入Promise
对象的resovle
函数,对应的,pending -> rejected
以后会去执行传入Promise
对象的reject
函数。
.then()
那resovle
函数和reject
函数是怎么传进去的呢,当然就是之前说的.then()
,.then()
可以接收两个参数,.then(onFulfilled[, onRejected])
这是官方写法,其实就是.then(resovle, reject)
,第一个参数是成功回调,第二个参数就是失败回调。如下⬇️
function timeout(isSuccess) {return new Promise((resolve, reject) => {if (isSuccess) {setTimeout(resolve, 1000)} else {reject()}})
}timeout(true).then(() => {console.log('成功')
}, () => {console.log('失败')
});timeout(false).then(() => {console.log('成功')
}, () => {console.log('失败')
});
复制代码
我用if语句模拟一下成功和失败的场景,这就是.then()
的用法。
.catch()
刚才说了.then()
的第二个参数传进去的是一个失败回调的函数,但是Promise
还有一个.catch()
的方法,也是用来处理失败的,例子如下⬇️:
function timeout() {return new Promise((resolve, reject) => {setTimeout(resolve, 1000)})
}timeout().then(() => {throw new Error('因为被凯多打败了,所以没当上海贼王')
}).catch((err) => {console.log('失败原因:', err)
});
复制代码
这时候也会输出错误信息。这时候你可能会问,那.then(resovle, reject)
的reject
和.catch(reject)
有什么区别呢,下面是个人见解
.then(resovle, reject)
的reject
和.catch(reject)
有什么区别
我个人认为,.then(resovle, reject)
的reject
按就近原则,只对最近的这个异步函数进行错误处理,但是对以后的或者之前的异步函数不做处理,而.catch(reject)
会捕获到全局所有链式上异步函数的错误。链式调用下面会讲到。总之就是.catch(reject)
管的范围要大一些。
链式调用
Promise
有一个对象链,并且这个对象链式呈流水线的模式进行作业,是因为在Promise
对象对自身的onFulfilled
和onRejected
相应器的处理中,会对其中返回的Promise
对象进行处理。其中内部会将这个新的Promise
对象加入到Promise
对象链中,并将其暴露出来,使其继续接受新的Promise
对象的加入。只有当Promise
对象链中的上一个Promise
对象进入成功或者失败阶段,下一个Promise
对象菜户被激活,这就形成了流水线的作业模式。
这就好比一开始使用Promise
改造回调地狱函数时候的样子⬇️
// 封装一层函数
function timeout() {return new Promise((resolve, reject) => {setTimeout(resolve, 1000)})
}
// 按回调函数的逻辑执行
timeout().then(() => {console.log(1);return timeout()
}).then(() => {console.log(2);return timeout()
}).then(() => {console.log(3);
});
复制代码
可以一层一层的传一下去,这也是厉害的地方。当链式调用中用.catch()
捕获错误的时候是这样的⬇️
function timeout() {return new Promise((resolve, reject) => {setTimeout(resolve, 1000)})
}timeout().then(() => {console.log(1);return timeout(err)}).then(() => {throw new Error('发生错误了')return timeout(2)}).catch((err) => {console.log('123',err)}).then(() => {console.log(3);});
复制代码
这种情况,.catch()
紧跟在抛出错误的一步函数后面,会抛出错误,然后继续往下执行,但是如果.catch()
是在最后,结果就完全不一样了⬇️
function timeout() {return new Promise((resolve, reject) => {setTimeout(resolve, 1000)})
}timeout().then(() => {console.log(1);return timeout(err)}).then(() => {throw new Error('发生错误了')return timeout(2)}).then(() => {console.log(3);}).catch((err) => {console.log('123',err)});
复制代码
如果是这样,前面说了.catch()
会捕获全局错误,但是,.catch()
写在最后,抛出错误以后,函数会直接跳到.catch()
然后继续往下执行,就像下面代码⬇️
function timeout() {return new Promise((resolve, reject) => {setTimeout(resolve, 1000)})
}timeout().then(() => {console.log(1);return timeout()}).then(() => {console.log(11);throw new Error('发生错误了')return timeout()}).then(() => {return timeout(2)}).catch((err) => {console.log('2',err)}).then(() => {throw new Error('发生错误了2')console.log(3);}).catch((err) => {console.log('3',err)});
复制代码
上面这段代码就会直接跳过输出2的异步函数,直接走到第一个.catch()
,然后再往下执行。
Promise高级
Promise.all()
这个方法真的太实用了,比如你进入首页,需要同时请求各种分类,用户信息等等信息,咱们可能需要在所有的请求都回来以后再展示页面,因为我们不能确定每个请求都要多久才能请求回来,所以这个问题一度很难解决。现在有了Promise.all()
这个方法,真的太方便了,下面就是例子⬇️
// Promise.all()需要传入的就是一个数组,每一项就是每一个异步函数
function timeout(delay) {return new Promise((resolve, reject) => {setTimeout(resolve, delay * 1000)})
}Promise.all([timeout(1),timeout(3),timeout(5),
]).then(() => {console.log('都请求完毕了!')
});
复制代码
上面代码会在最大延迟的5秒后然后在执行.then()
的方法,当然还有一个差不多的函数,往下看
Promise.race()
Promise.race()
会监听所有的Promise
对象,在等待其中的第一个进入完成状态的Promise
对象。一旦有第一个Promise
对象进入了完成状态,该方法返回的Promise
对象便会根据这第一个完成的Promise
对象的状态而改变,如下⬇️
function timeout(delay) {return new Promise((resolve, reject) => {setTimeout(resolve, delay * 1000)})
}Promise.race([timeout(1),timeout(3),timeout(5),
]).then(() => {console.log('有一个请求已经结束!')
});
复制代码
上面代码在执行1秒后就会执行.then()
的方法,然后剩下的两个请求继续等待返回。
反正我也没遇到过什么使用场景,知道有这个方法就行了
只管把目标定在高峰,人家要笑就让他去笑!
写到后面有点太官方的感觉,但是又觉得很不好解释,只能堆例子来解释了,跟大佬的差距还是有一定的差距,这只是基于我现在的水平到目前为止对Promise
的理解。
一句承诺,就要努力去兑现。自己选择的路,跪着也要走完。
我是前端战五渣,一个前端界的小学生。
转载于:https://juejin.im/post/5c91ccb7f265da60c95b66ef
前端战五渣学JavaScript——Promise相关推荐
- 前端战五渣学JavaScript——防抖、节流和rAF
看了<JavaScript高级程序设计>和网上的一些博客,感觉对函数节流和函数防抖的概念是反的,以下我写的关于防抖和节流的概念取决于多数人的概念吧,并且基于伦敦前端工程师David Cor ...
- 前端战五渣学JavaScript——call、apply以及bind
写这篇博客之前,我想先说下今天(2019年3月28日)一直关注的一件事吧(出于凑热闹的心情--尴尬).在昨天,全球最大交友网站Github上悄然出现一个名为996.ICU的文档项目,整个项目没有代码, ...
- 前端战五渣学JavaScript——闭包
就决定是你了--闭包 有不少开发人员总是搞不清匿名函数和闭包两个概念,因此经常混用.闭包是指有权访问另一个函数作用域中的变量的函数.创建闭包的常见方式,就是在一个函数内部创建另一个函数. ------ ...
- 前端战五渣学JavaScript——void 运算符
最近有点忙,公司有个新项目要尽快上线,所以工作时间很长,没有太多的时间去学习和总结,所以博客也没空更新了.但是充足的工作量让自己觉得很充足,沉淀的知识也有了用武之地,还不错.今天就写写这两天突然想到的 ...
- 前端战五渣学JavaScript——前端数据存储
说起前端数据存储,那就不得不提到Cookie.sessionStorage和localStorage,这是我们接触到的最熟悉的前端数据存储的单词,作为一个前端开发.前端工程师,这三个单词用没用过先不管 ...
- 你不知道的javascript_为什么前端要学JavaScript?JS都有哪些逆天的功能?
相信每一个想要学习前端的人对于前端三剑客html+css+js都不会陌生,这些可以说是一个前端的标配,而在前端的世界里,没有什么是JavaScript实现不了的,你真的了解js吗? 关于JS有一句话: ...
- 前端战五渣学React——JSX React.createElement() React.ReactElement()源码
最近<一拳超人>动画更新第二季了,感觉打斗场面没有第一季那么烧钱了,但是剧情还挺好看的,就找了漫画来看.琦玉老师真的厉害!!!打谁都一拳,就喜欢看老师一拳把那些上来就吹牛逼的反派打的稀烂, ...
- ashx获取input file 文件_前端战五渣学前端——FileReader预览本地文件
距离上一篇博客将近一个半月了,这一个多月有点烦躁,静不下心来学习,也不知道为什么,玩的也不算太好,还感冒.可能是天气热了吧,有点点躁动.上周看了<哪吒--魔童降世>还不错,还看了新出的&l ...
- 前端战五渣学前端——跨域
跨域(Cross-Origin) 这几年的工作.面试中,还遇到一个不能回避的问题,那就是跨域,一般面试的时候会问你'怎么解决跨域问题','跨域是什么','为什么会有跨域'等类似的问题.前段时间后端大佬 ...
最新文章
- html 表单内容怎么获取不到,jquery中formdate一直获取不到对象中的[0]的值 包括本身也是一个空的数据怎么办?...
- CSDN博客生成目录
- 人刚毕业,在小米造狗没有KPI,雷军奖励100万美金
- 在matlab中如何使用SVM工具箱
- 算法提高课-数学知识-矩阵乘法-AcWing 1303. 斐波那契前 n 项和:矩阵乘法,快速幂,线性代数
- 最新版FreeTextBox(版本3.1.6)在ASP.Net 2.0中使用简解(提供博客园本地下载)
- python 在gui中显示logging_如何在GUI中显示print()的输出python
- 知识点 组合数学 卡特兰数
- 1-概述程序构成基本元素
- mysql zip win10安装_mysql 8.0.16 Win10 zip版本安装配置图文教程
- 防爆技术在工业电子秤中的最新应用(转)
- 别在分答上问我问题了
- Mac端将本地新项目上传到github
- 首涂模板第四套还原经典挖片苹果cmsv10自适应主题模板
- 前端跨域解决方案总结
- JavaScript学习手册一:JS简介
- 初学者学习JS很吃力怎么办?到底该如何学习JS?
- 掌握销售谈判三大策略,开单成功率提升60%!
- Appium基础操作
- 【Android】技术调研:用代码模拟屏幕点击、触摸事件
热门文章
- 网线插座之一种的内部构造猜测
- PDCA 计划 执行 检查 反馈(处理)
- heic格式转换工具
- 计算机网络竞赛备赛安排表,2018年辽宁省职业院校技能大赛(高职组)计算机网络赛项须知...
- WiFi万能钥匙用户破九亿 陈大年称不想“穿越”回去
- SQL SERVER FOR XML PATH 用法
- 半监督kmeans聚类
- Ultra DMA CRC 错误计数 【警告】
- 笔记本 SSD+机械硬盘,使用中机械硬盘丢失
- python财经数据接口包_Tushare|免费开源财经数据接口包