文章目录

  • 前言
  • 一、回调地狱是什么?
  • 二、如何解决回调地狱
    • 1.Promise
    • 2.async/await
  • 总结

前言

在正式了解“回调地狱”之前,我们先了解两个概念:

  1. 回调函数
    当一个函数作为参数传入另一个参数中,并且它不会立即执行,只有当满足一定条件后该函数才可以执行,这种函数就称为回调函数。我们熟悉的定时器和Ajax中就存在有回调函数:
setTimeout(function(){   //function(){console.log('执行了回调函数')}就是回调函数,它只有在3秒后才会执行console.log('执行了回调函数');
},3000)  //3000毫秒

这里的回调函数是function(){console.log('执行了回调函数')},在满足时间3秒后执行。

//1.创建异步对象var xhr=new XMLHttpRequest();//2.绑定监听事件(接收请求)xhr.onreadystatechange=function(){//此方法会被调用4次//最后一次,readyState==4//并且响应状态码为200时,才是我们要的响应结果 xhr.status==200if(xhr.readyState==4 && xhr.status==200){//把响应数据存储到变量result中var result=xhr.responseText;console.log(result);}}//3.打开链接(创建请求)xhr.open("get","/demo/ajaxDemo",true);//4.发送请求xhr.send();

这里的回调函数是xhr.onreadystatechange绑定的函数,在xhr.send()发送请求并拿到响应后执行。

  1. 异步任务
    与之相对应的概念是“同步任务”,同步任务在主线程上排队执行,只有前一个任务执行完毕,才能执行下一个任务。异步任务不进入主线程,而是进入异步队列,前一个任务是否执行完毕不影响下一个任务的执行。同样,还拿定时器作为异步任务举例:
        setTimeout(function(){console.log('执行了回调函数');},3000)console.log('111');

如果按照代码编写的顺序,应该先输出“执行了回调函数”,再输出“111”。但实际输出为:

这种不阻塞后面任务执行的任务就叫做异步任务。
接下来让我们看看什么是回调地狱。

一、回调地狱是什么?

根据前面我们可以得出一个结论:存在异步任务的代码,不能保证能按照顺序执行,那如果我们非要代码顺序执行呢?

比如我要说一句话,语序必须是下面这样的:武林要以和为贵,要讲武德,不要搞窝里斗。
我必须要这样操作,才能保证顺序正确:

        setTimeout(function () {  //第一层console.log('武林要以和为贵');setTimeout(function () {  //第二程console.log('要讲武德');setTimeout(function () {   //第三层console.log('不要搞窝里斗');}, 1000)}, 2000)}, 3000)

可以看到,代码中的回调函数套回调函数,居然套了3层,这种回调函数中嵌套回调函数的情况就叫做回调地狱。

总结一下,回调地狱就是为是实现代码顺序执行而出现的一种操作,它会造成我们的代码可读性非常差,后期不好维护。

那该如何解决回调地狱呢?

二、如何解决回调地狱

1.Promise

Promise是js中的一个原生对象,是一种异步编程的解决方案,可以替换掉传统的回调函数解决方案。

  1. Promise构造函数接收一个函数作为参数,我们需要处理的异步任务就卸载该函数体内,该函数的两个参数是resolve,reject。异步任务执行成功时调用resolve函数返回结果,反之调用reject。
  2. Promise对象的then方法用来接收处理成功时响应的数据,catch方法用来接收处理失败时相应的数据。
  3. Promise的链式编程可以保证代码的执行顺序,前提是每一次在than做完处理后,一定要return一个Promise对象,这样才能在下一次then时接收到数据。
    下面是实例代码:
        function fn(str){var p=new Promise(function(resolve,reject){//处理异步任务var flag=true;setTimeout(function(){if(flag){resolve(str)}else{reject('操作失败')}})})return p;}fn('武林要以和为贵').then((data)=>{console.log(data);return fn('要讲武德');}).then((data)=>{console.log(data);return fn('不要搞窝里斗')}).then((data)=>{console.log(data);}).catch((data)=>{console.log(data);})


但是Promise最大的问题就是代码冗余,原来的异步任务被Promise封装一下,不管什么操作都用than,就会导致一眼看过去全是then…then…then…,这样也是不利于代码维护的。

所以下面的async/await 可以时代码看起来更像同步代码。

2.async/await

首先我们看async关键字,他作为一个关键字放到声明函数前面,表示该函数为一个异步任务,不会阻塞后面函数的执行:

        async function fn(){return '不讲武德';}console.log(fn());


可以看到async函数返回数据时自动封装为一个Promise对象。

和Promise对象一样,处理异步任务时也可以按照成功和失败来返回不同的数据,处理成功时用then方法来接收,失败时用catch方法来接收数据:

        async function fn() {var flag = true;if (flag) {return '不讲武德';}else{throw '处理失败'}}fn().then(data=>{console.log(data);}).catch(data=>{console.log(data);})console.log('先执行我,表明async声明的函数是异步的');


当把flag设置为false是,执行结果为:

async关键字说完了,我们看看awai关键字

  • await关键字只能在使用async定义的函数中使用
  • ​await后面可以直接跟一个 Promise实例对象(可以跟任何表达式,更多的是跟一个返回Promise对象的表达式)
  • await函数不能单独使用
  • await可以直接拿到Promise中resolve中的数据。
        //封装一个返回promise的异步任务function fn(str) {var p = new Promise(function (resolve, reject) {var flag = true;setTimeout(function () {if (flag) {resolve(str)} else {reject('处理失败')}})})return p;}//封装一个执行上述异步任务的async函数async function test(){var res1=await fn('武林要以和为贵');  //await直接拿到fn()返回的promise的数据,并且赋值给resvar res2=await fn('要讲武德');var res3=await fn('不要搞窝里斗');console.log(res1,res2,res3);}//执行函数test();

结果为:

为什么叫await等待呢,因为当代码执行到async函数中的await时,代码就在此处等待不继续往下执行,知道await拿到Promise对象中resolve的数据,才继续往下执行,这样就保证了代码的执行顺序,而且使异步代码看起来更像同步代码。

总结

总结一下,当我们写代码遇到异步回调时,我们想让异步代码按照我们想要的顺序执行,如果按照传统的嵌套方式,就会出现回调地狱,这样的代码不利于维护,我们可以通过Promise对象进行链式编程来解决,这样尽管可以解决问题,但是ES7给我们提供了更加舒适的async/await语法糖,可以使得异步代码看起来更像是同步代码。

一文告诉你什么是回调地狱,如何解决回调地狱?相关推荐

  1. 回调地狱以及解决回调地狱 - promise嵌套变链接 - 解决终极办法 - async 和 await

    回调函数? 当一个函数被当做参数传递时,这个函数就叫做回调函数-  callback 通常使用回调函数来处理异步代码 当异步代码执行结束后,需要执行的代码就要放在回调函数中 回调地狱? 当回调函数嵌套 ...

  2. 什么是回调地狱?解决回调地狱的方法

    什么是回调地狱?(代码执行顺序的一种操作(解决异步)) 回调地狱的概念:回调函数里面嵌套回调函数. //地狱回调setTimeout(function () { //第一层console.log('1 ...

  3. 回调地狱,解决回调地狱,回调地狱的终极解决方案

    回调地狱示例 使用promise的链式调用解决回调地狱 async/await:回调地狱的终极解决方案

  4. 回调地狱及解决回调地狱的终极目标

    目录 一.回调地狱的定义 二.解决回调地狱的终极目标 三.promise语法 3.1  定义 3.2  封装promise函数 3.3  语法形式 四.async 和 await语法 4.1  定义 ...

  5. promise解决回调地狱的问题

    什么是回调地狱 怎么解决回调地狱的问题 要了解什么是回调地狱,首先要了解 什么是同步,什么是异步函数 同步函数:当一个函数是同步执行时,那么当该函数被调用时不会立即返回,直到该函数要做的事情全部做完之 ...

  6. 回调函数与回调地狱及其解决方法 | JavaScript

    JavaScript中的回调函数与回调地狱及其解决方法 以下为整理的思维导图 图片版+文字版 文末附有参考文章链接 知识点思维导图版 一.什么是回调函数 1.MDN的定义 回调函数是作为参数传给另一个 ...

  7. 什么是回调函数?回调函数有什么缺点?如何解决回调地狱问题?

    什么是回调函数?回调函数有什么缺点?如何解决回调地狱问题? 参考文章: (1)什么是回调函数?回调函数有什么缺点?如何解决回调地狱问题? (2)https://www.cnblogs.com/kzxi ...

  8. JavaScript中的回调地狱及解决方法

    JavaScript中的回调地狱及解决方法 1.回调地狱 在使用JavaScript时,为了实现某些逻辑经常会写出层层嵌套的回调函数,如果嵌套过多,会极大影响代码可读性和逻辑,这种情况也被成为回调地狱 ...

  9. 一文告诉你如何导出 Git 变更文件

    转载自   一文告诉你如何导出 Git 变更文件 有时候我们想导出某次版本提交时有哪些变更的文件,在 svn 中有一个 export 功能,很方便,如下图所示. 在 Git 中我也找到了以下两种方法. ...

最新文章

  1. vue脚手架---vue-cli
  2. centos7 如何安装部署k8s_五步教你如何使用k8s快速部署ES
  3. 空间谱专题08:相位模糊
  4. 如何安装并启动django
  5. 同步容器和并发容器的区别
  6. 什么是 Python?我介绍我几年前学习Python的方法和经验
  7. 返回值 包装类_java中基本类型包装类
  8. springboot版本兼容
  9. AttributeError: 'function' object has no attribute 'Document'报错解决
  10. java面向对象传播智客,传智播客java培训 java基础之javabean
  11. 中学计算机基础授课ppt,高中信息技术说课课件.ppt
  12. 用Python生成Hilbert矩阵
  13. 修改注册表阻止Office XP发送错误报告
  14. 制作rime配色的fcitx皮肤
  15. EMM系列1:EMM和ECM状态
  16. 防蓝光膜能减小手机对眼睛的伤害吗?
  17. OSChina 周三乱弹 —— 国家命运与个人命运
  18. 听鬼哥说故事之U3D游戏破解分析
  19. 汇千网-赴港上市升温 忙到日程排不下 港交所“红利期”来了
  20. go break跳出两层for循环

热门文章

  1. html5 仿手机聊天,HTML5仿手机微信聊天界面
  2. VLAN基础与划分广播域
  3. 什么叫泛型?有什么作用?
  4. Java 泛型的作用以及类、方法、接口的使用和通配符
  5. java 抽象泛型_java-使用泛型定义抽象方法
  6. 玩转输入法,打出不一样的符号
  7. 论文阅读——WaveNet: A Generative Model for Raw Audio
  8. Qt之按钮添加背景图片的几种方法
  9. Sqlserver 字符串截取函数
  10. 区块链9999999666666