回调地狱 与 Promise(JavaScript)

news2025/1/12 18:07:57

目录捏

  • 前言
  • 一、异步编程
  • 二、回调函数
  • 三、回调地狱
  • 四、Promise
    • 1. Promise 简介
    • 2. Promise 语法
    • 3. Promise 链式
  • 五、总结


在这里插入图片描述

前言

想要学习Promise,我们首先要了解异步编程回调函数回调地狱三方面知识:

一、异步编程

异步编程技术使你的程序可以在执行一个可能长期运行的任务的同时继续对其他事件做出反应而不必等待任务完成。
与此同时,你的程序也将在任务完成后显示结果。

在这里插入图片描述
举个栗子:

假设现在老板让你修改一个很紧急并且很重要的代码,让你下班前必须改完。并且为了督促进度,老板搬了个椅子坐在一边盯着你敲。

你心里肯定已经犯嘀咕:“你有这么闲吗?就不能去干点其他事情吗?”

老板仿佛接收到了你的心电图一样:“我就在这等着,你改完代码之前我哪也不去。”

这个例子中老板交给你任务后就一直等待什么都不做直到你改完,这个场景就是所谓的同步

第二天,老板又交给了你一项任务。

不过这次就没那么着急啦,这次老板轻描淡写“今天的这个代码不着急,你写完告诉我一声就行。”

这次老板没有盯着你写代码而是转身刷视频去了,你写完后简单的和老板报告了一声“我写完啦!”

这个例子老板交代完任务就去忙其它事情,你完成任务后简单的告诉老板任务完成,这就是所谓的异步

值得注意的是:在异步这种场景下你在改代码的同时老板在刷视频,这两件事在同时进行因此这就是异步比同步高效的本质


异步任务相对应的概念是同步任务,同步任务在主线程上排队执行,只有前一个任务执行完毕,才能执行下一个任务。异步任务不进入主线程,而是进入异步队列,前一个任务是否执行完毕不影响下一个任务的执行。这里拿定时器作为异步任务举例:

// setTimeout中的内容不会先被输出,而是先输出异步任务之后的内容
    setTimeout(() => {
        console.log('我在定时器里捏!!')
    }, 2000)
    console.log('我在定时器后捏~~')

如果按照代码编写的顺序,应该先输出我在定时器里捏!!,再输出我在定时器后捏~~。但实际输出为:

在这里插入图片描述

这种不阻塞后面任务执行的任务就叫做异步任务

二、回调函数

把一个函数当作参数传递给另一个函数,但是此函数并不会立即执行,而是在将来特定的时机再去调用,这个函数就叫做回调函数。在定时器setTimeout以及Ajax的请求时都会用到回调函数。

在这里插入图片描述

再举个栗子:

你到一个商店去买东西,刚好你要的东西没有货,于是你在店员那里留下了你的电话,过了几天店里有货了,店员就打了你的电话,然后你接到电话后就到店里去取了货。

在这个例子里,你的电话号码就叫回调函数,你把电话留给店员就叫登记回调函数,店里后来有货了叫做触发了回调关联的事件,店员给你打电话叫做调用回调函数,你到店里去取货叫做响应回调事件。


回调函数在我们启动一个异步任务的时候就会告诉它:等你完成了这个任务之后要干什么。这样一来主线程几乎不用关心异步任务的状态了,他自己会善始善终。

// setTimeout中第一个参数就是回调函数,只有在1秒后执行
    setTimeout(() => {
        console.log('执行回调函数!!')
    }, 1000);
    console.log('先执行我捏~~')

根据前面对异步任务的介绍,应该知道此代码会先执行定时器后的语句,再执行定时器及回调函数

在这里插入图片描述

三、回调地狱

根据前面对异步编程以及回调函数的介绍我们可以得出一个结论:存在异步任务的代码,不能保证其按照顺序执行,那如果我们非要代码顺序执行呢?

比如我要间隔不同时间输出三句话,语序必须是下面这样的:我在定时器1里捏!!我在定时器2里捏!!我在定时器3里捏!!

    setTimeout(() => {
        console.log('我在定时器1里捏!!')
    }, 3000)
    setTimeout(() => {
        console.log('我在定时器2里捏!!')
    }, 2000)
    setTimeout(() => {
        console.log('我在定时器3里捏!!')
    }, 1000)
    console.log('我在定时器后捏~~')

当使用定时器顺序调用时,则会出现输出顺序错乱的问题:

在这里插入图片描述

所以必须要这样操作,才能保证输出顺序正确:

    setTimeout(() => {
        console.log('我在定时器1里捏!!')
        setTimeout(() => {
            console.log('我在定时器2里捏!!')
            setTimeout(() => {
                console.log('我在定时器3里捏!!')
            }, 1000)
        }, 2000)
    }, 3000)
    console.log('我在定时器后捏~~')

在这里插入图片描述

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

在这里插入图片描述

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

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

四、Promise

1. Promise 简介

Promise,中文翻译过来就是承诺,意思是承诺在未来某一个时间点返回数据给你。它是JS中的一个原生对象,是一种异步编程的解决方案,可以替换掉传统的回调函数解决方案。

首先,Promise 对象有三个状态:pending(进行中),fulfilled(已成功),rejected(已失败)

其次,Promise 构造函数接收一个函数作为参数,该函数是同步的并且会被立即执行,所以我们称之为起始函数,我们需要处理的异步任务就卸载在该函数体内,该函数的两个参数是resolvereject。异步任务执行成功时调用resolve函数并传递成功的结果,反之调用reject并传递失败的原因。

最后,Promise 构造函数返回一个 Promise 对象,该对象具有以下几个方法:

  • then:用于处理 Promise 成功状态的回调函数。
  • catch:用于处理 Promise 失败状态的回调函数(有任何异常都会直接执行)。
  • finally:无论 Promise 是成功还是失败,都会执行的回调函数。

在这里插入图片描述

2. Promise 语法

Promise 本身只是一个容器,真正异步的是它的两个回调resolvereject,分别表示 Promise 成功失败的状态。其本质不是控制异步代码的执行顺序 ,而是控制异步代码结果处理的顺序

那么如何改变 Promise 的状态:

  • resolve(value): 如果当前是 pending 就会变为 fulfilled
    const p = new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('用户数据读取成功!!')
        }, 1000)
    })

    p.then(value => {
        console.log(value)
    }).catch(reason => {
        console.log(reason)
    })

	console.log(p)

在这里插入图片描述

  • reject(error): 如果当前是 pending 就会变为 rejected
    const p = new Promise((resolve, reject) => {
        setTimeout(() => {
            reject('用户数据读取失败~~')
        }, 1000)
    })

    p.then(value => {
        console.log(value)
    }).catch(reason => {
        console.log(reason)
    })

	console.log(p)

在这里插入图片描述

  • 抛出异常: 如果当前是 pending 就会变为 rejected
    const p = new Promise((resolve, reject) => {
    	throw new Error('出错啦!!')
    })
    
    console.log(p)

在这里插入图片描述

注意:一旦从进行状态变成为其他状态就永远不能更改状态了。

3. Promise 链式

Promise 链式编程可以保证代码的执行顺序,前提是每一次在than做完处理后,一定要 return 一个 Promise对象,这样才能在下一次then时接收到数据。

在对 Promise 有了一定了解之后,再尝试通过 Promise 链式调用来解决上文介绍回调地狱时所提出的问题,实现以下语句:我在定时器1里捏!!我在定时器2里捏!!我在定时器3里捏!!

    new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('我在定时器1里捏!!')
        }, 3000);
    }).then(value => {
        console.log(value)
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve('我在定时器2里捏!!')
            }, 2000);
        })
    }).then(value => {
        console.log(value)
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve('我在定时器3里捏!!')
            }, 1000);
        })
    }).then(value =>
        console.log(value)
    ).catch(reason =>
        console.log(reason))

在这里插入图片描述

上述代码看上去很凌乱,可读性并不好,所以我们可以将它的核心部分写成一个 promise 函数

    function promise(value, time) {
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve(value)
            }, time)
        })
    }

    promise('我在定时器1里捏!!', 3000)
        .then(data => {
            console.log(data);
            return promise('我在定时器2里捏!!', 2000);
        })
        .then(data => {
            console.log(data);
            return promise('我在定时器3里捏!!', 1000)
        })
        .then(data => {
            console.log(data);
        })
        .catch(data => {
            console.log(data);
        })

在这里插入图片描述

五、总结

Promise 虽然摆脱了回调地狱,但是then的链式调用也会带来额外的阅读负担,并且 Promise 传递中间值非常麻烦。

同时, Promise 的调试很差,由于没有代码块,你不能在⼀个返回表达式的箭头函数中设置断点,如果你在⼀个then代码块中使用调试器的步进step-over功能,调试器并不会进入后续的then代码块,因为调试器只能跟踪同步代码的每⼀步。

所以ES2017推出了新的语法 async/await 来更好的解决异步问题,下一篇文章会给大家带来async/await的相关介绍,敬请期待~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1198352.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

帧同步的思想与FIFO复位

02基于FDMA三缓存构架_哔哩哔哩_bilibili 图像从外部传输进来的时候,会产生若干延迟,可能会出现各种各样的问题(断帧等),此时可以通过VS信号清空FIFO进行复位。 这个过程中的复位信号可能需要拓展,这是因为…

mysql 讲解(1)

文章目录 前言一、基本的命令行操作二、操作数据库语句2.1、创建数据库2.2、删除数据库2.3、使用数据库2.4 查看所有数据库 三、列的数据类型3.1 字符串3.2 数值3.3 时间日期3.4 空3.5 int 和 varchar问题总结: 四、字段属性4.1 UnSigned4.2 ZEROFILL4.3 Auto_InCre…

【python海洋专题四十六】研究区域示意放大图

【python海洋专题四十六】研究区域示意放大图 图片 往期推荐 图片 【python海洋专题一】查看数据nc文件的属性并输出属性到txt文件 【python海洋专题二】读取水深nc文件并水深地形图 【python海洋专题三】图像修饰之画布和坐标轴 【Python海洋专题四】之水深地图图像修饰 …

Vuex:模块化Module :VOA模式

由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。 这句话的意思是,如果把所有的状态都放在/src/store/index.js中,当项目变得越来越大的时候,Vue…

国际阿里云:无法访问ECS实例中的服务的排查方法!!!

操作场景 无法访问ECS实例中的服务可能有以下原因: 可能原因 排查方案 ECS实例的安全组未开放相应端口 检查ECS实例安全组规则 ECS实例中,该服务未启动/开启或服务对应端口未被监听 检查服务状态及端口监听状态 ECS实例内防火墙设置错误 检查ECS…

Dubbo从入门到上天系列第五篇:Dubbo3与JDK17不兼容问题展示

文章目录 一:JDK 与 Dubbo版本对应问题说明 1:问题1 2:问题2 二:Spring与JDK版本对应关系 1:对应关系详图 2:JDK与Major对应关系图 大神链接:作者有幸结识技术大神孙哥为好友&#xff0c…

SW如何显示样条曲线的控标

刚刚学习隔壁老王的sw画图时,怎么点都点不出样条曲线的控标,于是果断查询了一下解决方法,其实很简单,只不过是培训机构故意不说,叫你还解决不了,难受了就会花钱买他们的课了。毕竟如果学会了怎么解决问题了…

5G+智慧港口建设解决方案

一、智慧港口建设背景 智慧港口是随着时代进步发展起来的一种现代港口运输的新业态,它是以现代化基础设施为基础,促使大数据、云计算、物联网、移动互联网、智能控制等新一代信息技术与港口运输业务深度融合,以港口运输组织服务创新为动力&am…

【开源】基于Vue.js的生活废品回收系统的设计和实现

目录 一、摘要1.1 项目介绍1.2 项目详细录屏 二、研究内容三、界面展示3.1 登录注册3.2 资源类型&资源品类模块3.3 回收机构模块3.4 资源求购/出售/交易单模块3.5 客服咨询模块 四、免责说明 一、摘要 1.1 项目介绍 生活废品回收系统是可持续发展的解决方案,旨…

高防IP是什么?有什么优势?

一.高防IP的概念 高防IP是指高防机房所提供的IP段,一种付费增值服务,主要是针对网络中的DDoS攻击进行保护。用户可以通过配置高防IP,把域名解析到高防IP上,引流攻击流量,确保源站的稳定可靠。 二.高防IP的原理 高防I…

Python异常处理:三种不同方法的探索与最佳实践

Python异常处理:三种不同方法的探索与最佳实践 前言 本文旨在探讨Python中三种不同的异常处理方法。通过深入理解各种异常处理策略,我们可以更好地应对不同的编程场景,选择最适合自己需求的方法。 异常处理在编程中扮演着至关重要的角色。合…

智慧城市数据中台建设方案:PPT全文51页,附下载

关键词:智慧城市解决方案,数据中台解决方案,智慧城市建设,数据中台技术架构,数据中台建设 一、智慧城市数据中台建设背景 智慧城市数据中台是在城市数字化转型和智能化升级的背景下提出的,旨在实现城市数…

ElasticSearch知识点

什么是ElasticSearch ElasticSearch: 智能搜索,分布式的搜索引擎,是ELK的一个非常完善的产品,ELK代表的是: E就是ElasticSearch,L就是Logstach,K就是kibana Elasticsearch是一个建立在全文搜索引擎 Apache Lucene基础…

腾讯云3年期轻量应用服务器优惠(薅羊毛教程)

腾讯云轻量应用服务器特价是有新用户限制的,所以阿腾云建议大家选择3年期轻量应用服务器,一劳永逸,免去续费困扰。腾讯云轻量应用服务器3年优惠可以选择2核2G4M和2核4G5M带宽,3年轻量2核2G4M服务器540元,2核4G5M轻量应…

封神教程:腾讯云3年轻量应用服务器老用户购买方法

腾讯云轻量应用服务器特价是有新用户限制的,所以阿腾云建议大家选择3年期轻量应用服务器,一劳永逸,免去续费困扰。腾讯云轻量应用服务器3年优惠可以选择2核2G4M和2核4G5M带宽,3年轻量2核2G4M服务器540元,2核4G5M轻量应…

vscode因为大文件而无限崩溃的问题,窗口意外终止(原因:“oom“,代码:“-536870904“

复制了一大堆的代码(好几兆)到一个文件里,然后就导致 vscode 卡死, 之后就算把该文件删掉了,打开vscode还是会默认打开该文件而卡死 解决办法: win R 输入 %appdata%/code/ 删除该文件夹下的 backups/ 文件…

【m98】webrtc vs2017构建带符号的debug库

调试有符号 调试 无符号 试试exe不输出到独立的文件? -】 直接输出到sln下面

在 Microsoft Word 中启用护眼模式

在 Microsoft Word 中启用护眼模式 在使用 Microsoft Word 365 或 Word 2019(Windows)版本时,启用护眼模式(也称为“夜间模式”)可以有效减轻屏幕亮度,有助于减少眼睛疲劳。以下是启用护眼模式的步骤&…

ChatGPT+Roblox,元宇宙的AI叙事逻辑#Leveling Up

MixCopilot 嗨,亲爱的听众朋友们!欢迎收听我们的播客节目!我是你们的主播:MixCopilot 混合副驾。今天我们要为大家带来的是我们的AI革命系列节目之一。这个系列节目聚焦于AI领域的一些最有影响力的建设者,他们将会讨论…

Leetcode -463.岛屿的周长 - 476.数字的补码

Leetcode Leetcode -463.岛屿的周长Leetcode - 476.数字的补码 Leetcode -463.岛屿的周长 题目:给定一个 row x col 的二维网格地图 grid ,其中:grid[i][j] 1 表示陆地, grid[i][j] 0 表示水域。 网格中的格子 水平和垂直 方向…