我是要成为海贼王的男人

悟空已成神,鸣人已成影,待路飞成王之时,便是我青春结束时!

悟空陪布玛找寻龙珠,一路拳打比克、斩弗利萨,生个儿子战沙鲁,最后净化布欧,只因承诺要保护地球。鸣人“有话直说,说到做到,这就是我的忍道”,一句会把佐助带回来的承诺,断臂践行。路飞要凑齐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/APromise/A+等概念的出现,ES2015中的Promise标准便源自于Promise/A+Promise最大的目的在于可以让异步函数变得竟然有序,就如我们需要在浏览器中访问一个JSON座位返回格式的第三方API,在数据下载完成后进行JSON解码,通过Promise来包装异步流程可以使代码变得非常干净。———————摘自《实战ES2015》

上面最重要的一句就是可以让异步函数变得竟然有序,可能有人会说awaitasync也可以让异步函数同步执行,但是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构造函数要求传入一个函数,并带有resovlereject参数。一个成功回调函数,一个失败成功回调函数。下面是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对象对自身的onFulfilledonRejected相应器的处理中,会对其中返回的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的理解。

一句承诺,就要努力去兑现。自己选择的路,跪着也要走完。


我是前端战五渣,一个前端界的小学生。

前端为什么有的接口明明是成功回调却执行了.catch失败回调_前端战五渣学JavaScript——Promise...相关推荐

  1. 前端为什么有的接口明明是成功回调却执行了.catch失败回调_Web前端:ES6是干什么的?(下)...

    大家好,我来了!本期为大家带来的Web前端学习知识是"Web前端:ES6是干什么的?(下)",喜欢Web前端的小伙伴,一起看看吧! 主要内容 Promise Generator A ...

  2. 前端为什么有的接口明明是成功回调却执行了.catch失败回调_前端进阶高薪必看-手写源码篇(高频技术点)...

    前言 此系列作为笔者之前发过的前端高频面试整理的补充 会比较偏向中高前端面试问题 当然大家都是从新手一路走过来的 感兴趣的朋友们都可以看哈 初衷 我相信不少同学面试的时候最怕的一个环节就是手写代码 大 ...

  3. 前端为什么有的接口明明是成功回调却执行了.catch失败回调_前端知识整理

    css 异步编程: 传统的异步解决方案采用回调函数和事件监听的方式 新方案: ES6的新语法Promise promise就是一个容器,里面包含着一个未来才会结束的事件(通常是一个异步操作)的结果.比 ...

  4. webstorm前端调用后端接口_软件测试面试题:怎么去判断一个bug是前端问题还是后端问题...

    大家好,在软件测试面试过程中,经常有面试官问到这个问题,那我们应该如何回答才好呢?少废话,直接看答案: 答案: 在页面上发现bug之后,要想判断这个问题属于后端还是前端,我就需要来判断这个页面背后调用 ...

  5. H5的jsapi微信支付:wx.chooseWXPay,ios手机在支付成功后不执行success中的回调函数

    对于iOS客户端支付成功后不进入chooseWXPay函数success的问题原因是:目前没有得到解答 临时解决方案: 支付成功后,安卓和iOS的返回都是{"errMsg":&qu ...

  6. 前端中怎么把网页多个文件夹的内容整合成一个_web前端学习笔记

    web前端的定义:是面向用户(浏览者)的互联网技术统称.主要包括Web界面的结构.Web界面的外观视觉表现以及Web界面的交互实现. HTML结构语言:超文本标记语言. Web前端的分类:前端设计和前 ...

  7. Nginx解决前端调用后端接口跨域问题

    Nginx解决前端调用后端接口跨域问题 参考文章: (1)Nginx解决前端调用后端接口跨域问题 (2)https://www.cnblogs.com/wangymd/p/11200746.html ...

  8. 前端与后端接口的交互案例

    一.案例描述 1,前端页面提供用户名,密码输入框. 2,通过Ajax发送请求到后端Serlvet. 3,后端Serlvet处理请求,根据输入的用户名和密码返回给前端不同信息 前端访问后端接口通过后端提 ...

  9. Java回调网址_极光短信- 回调接口 - 极光文档

    回调接口 设置并校验回调地址: 回调消息格式说明: 测试回调功能的方法: 设置回调地址 功能说明 设置并校验回调地址 操作路径 Step1:登入控制台 Step2:进入应用模版 Step3:右侧菜单中 ...

  10. App前端及后端接口,模拟数据及返回值

    App前端及后端接口,模拟数据 :接口文档 目录 1. 全局状态码 6 2. 前台 7 2.1. 首页 7 2.1.1. 商品分类列表接口 7 2.1.2. 展示轮播图接口 9 2.1.3. 展示广告 ...

最新文章

  1. Can't toast on a thread that has not called Looper.prepare()
  2. struts2 select 默认选中
  3. 从0实现一个tinyredux
  4. 应用中验证码的生成方法.
  5. KindEditor编辑器在ASP.NET中的使用
  6. AI理论知识整理(16)-线性方程组有解
  7. leetcode15 3Sum 从数组中找到三个整数,它们的和为0
  8. jzoj6312-Lottery【dp,前缀和】
  9. CCIE-LAB-SDN-第二篇-DNAC中完成VN配置
  10. 【Python基础知识-pycharm版】第三节-列表
  11. vue webpack打包入口文件是哪个_Vue 学习笔记(二十五):webpack 相关
  12. 从零开始开发JVM语言(七)语义分析的起步
  13. java取set中的元素个数_java中的Set的使用以及各种遍历方法(较为全面)
  14. Python学习总结(3)——数字类型
  15. html5 歌词自动滚动效果,简单的HTML5音乐播放器(带歌词滚动)
  16. 软件开发几个阶段的内容以及产物
  17. Python的那些事
  18. 普通人现在入局做抖音短视频晚么 选择项目的标准是什么
  19. c语言错误L104,KEILC51编译问题ERROR L104
  20. APP运营推广人员必备通讯录

热门文章

  1. MySQL 修改字段类型或长度
  2. 领域驱动设计系列 (六):CQRS
  3. Convert.ToInt32、(int)和int.Parse 三者的区别 转
  4. Myeclipse修改代码提示框背景色
  5. 网吧的云计算机,云电脑和网吧开启线上网咖是新趋势?
  6. html微信窗口阻止滚动条,微信浏览器禁止页面下拉查看网址(不影响页面内部scroll)...
  7. java中foreach怎么访问_JAVA中的foreach怎么用
  8. Linux学习笔记6 - 用户和组群账户管理
  9. Linux字符设备和块设备的区别
  10. nyoj 236 心急的C小加(贪心)