回调地狱 与 Promise(JavaScript)
目录捏
- 前言
- 一、异步编程
- 二、回调函数
- 三、回调地狱
- 四、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 构造函数接收一个函数作为参数,该函数是同步的并且会被立即执行,所以我们称之为起始函数,我们需要处理的异步任务就卸载在该函数体内,该函数的两个参数是resolve
,reject
。异步任务执行成功时调用resolve
函数并传递成功的结果,反之调用reject
并传递失败的原因。
最后,Promise 构造函数返回一个 Promise 对象,该对象具有以下几个方法:
then
:用于处理 Promise成功
状态的回调函数。catch
:用于处理 Promise失败
状态的回调函数(有任何异常都会直接执行)。finally
:无论 Promise 是成功还是失败,都会执行的回调函数。
2. Promise 语法
Promise 本身只是一个容器,真正异步的是它的两个回调resolve
和reject
,分别表示 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
的相关介绍,敬请期待~
相关文章:

回调地狱 与 Promise(JavaScript)
目录捏 前言一、异步编程二、回调函数三、回调地狱四、Promise1. Promise 简介2. Promise 语法3. Promise 链式 五、总结 前言 想要学习Promise,我们首先要了解异步编程、回调函数、回调地狱三方面知识: 一、异步编程 异步编程技术使你的程序可以在执行一…...
【Android】UI开发中的一些小细节笔记
序言 本篇笔记用于记录在UI界面编写时的一些很简单但是可能一时想不起来的一些小的知识点。(持续更新…) 正文 TextView 1.当文字比较多,需要多行显示的时候,设置每行文字之间的上下的间距 android:lineSpacingExtra根据需要调整这个值设置行间距 …...

第十三章《搞懂算法:神经网络是怎么回事》笔记
目前神经网络技术受到追捧,一方面是由于数据传感设备、数据通信技术和数据存储技术 的成熟与完善,使得低成本采集和存储海量数据得以成为现实;另一方面则是由于计算能力的大幅提升,如图形处理器(Graphics Processing Unit,GPU)在神…...

SpringBoot不同环境加载不同配置文件(dev,sit,uat)
目录 一、springboot的profile配置profile多配置文件 二、maven的profiles策略 我们在使用spring的时候,一般都会有不同的环境需要部署:开发环境、测试环境和验收环境,而不同的环境则会有不同的配置,比如数据库ip。解决这个问题&a…...

2023.11.8 hadoop学习-概述,hdfs dfs的shell命令
目录 1.分布式和集群 2.Hadoop框架 3.版本更新 4.hadoop架构详解 5.页面访问端口 6.Hadoop-HDFS HDFS架构 HDFS副本 7.SHELL命令 8.启动hive服务 1.分布式和集群 分布式: 多台服务器协同配合完成同一个大任务(每个服务器都只完成大任务拆分出来的单独1个子任务)集 群:…...

Azure 机器学习 - 使用自动化机器学习训练计算机视觉模型的数据架构
目录 一、用于训练的数据架构图像分类(二进制/多类)多标签图像分类对象检测实例分段 二、用于联机评分的数据架构输入格式输出格式图像分类(二进制/多类)多标签图像分类对象检测实例分段 在线评分和可解释性 (XAI) 的数据格式支持…...

STM32F4X SDIO(九) 例程讲解-SD卡擦除、读写
STM32F4X SDIO (九) 例程讲解-SD卡擦除、读写 例程讲解-SD卡擦除、读写SD卡擦除CMD32:ERASE_WR_BLK_START命令发送命令响应 CMD33:ERASE_WR_BLK_END命令发送命令响应CMD38:ERASE命令响应 CMD13:SD_CMD_SEND_STATUS命令发送命令回应 SD卡读数据CMD16:SET_…...

【机器学习范式】监督学习,无监督学习,强化学习, 半监督学习,自监督学习,迁移学习,对比分析+详解与示例代码
目录 1. 监督学习 (Supervised Learning): 2. 无监督学习 (Unsupervised Learning): 3. 强化学习 (Reinforcement Learning): 4. 半监督学习 (Semi-Supervised Learning): 5. 自监督学习 (Self-Supervised Learning): 6. 迁移学习 (Transfer Learning): 7 机器学习范式应…...

JUC包下面的四大天王+线程池部分知识
一)Semphore:限流器用我就对了 Java中信号量Semphore是把操作系统原生的信号量封装了一下,本质就是一个计数器,描述了 可用资源的个数,主要涉及到两个操作 如果计数器为0了,继续Р操作,就会出现阻塞等待的情况 P操作:申…...
AGV系统控制位置管理功能
# ファイル: agv_locattion.py # 説明: AGV (Automated Guided Vehicle) の位置情報を管理し、UDPサーバーとして動作するGUIアプリケーションです。 # 必要なライブラリをインポート import tkinter as tk import socket import threading def AGV_handle_submit(canvas, st…...

JavaScript从入门到精通系列第三十三篇:详解正则表达式语法(二)
文章目录 一:正则表达式 1: 检查一个字符串中是否有. 2:第二种关键表达 3:第三种关键表达 编辑4:第四种关键表达 5:第五种关键表达 6:第六种关键表达 二:核心表达二 1&am…...

由于找不到 d3dx9_43.dll,无法继续执行代码。重新安装程序可能会解决此问题
电脑出现d3dx9_43.dll缺失的问题,通常是由于DirectX组件未安装或损坏导致的。为了解决这个问题,我为您提供了以下四个解决方法: d3dx9_43.dll解决方法1. 使用dll修复程序修复 首先,使用系统文件程序dll进行修复操作非常简单&…...
AI全栈大模型工程师(二十一)LangChain和SemanticKernel怎么选
LangChain 和 Semantic Kernel 怎么选? #%% md 划重点: 两者都值得学C#、JavaScript 和 Java 现在没得选做原型,首选 LangChain。功能多,开发快做产品,还是 SK 长期更可依赖建议只用 SK 的 Connectors 和 Plugins 能力…...

npm install 报错 chromedriver 安装失败的解决办法
npm install chromedriver --chromedriver_cdnurlhttp://cdn.npm.taobao.org/dist/chromedriver...
C语言--每日五道选择题--Day6
第一题 1、声明以下变量,则表达式: ch/i (f*d – i) 的结果类型为( ) char ch; int i; float f; double d; A: char B: int C: float D: double 答案及解析 D 基本数据类型的等级从低到高如下:char-> int-> long-> f…...
element-ui 封装 表格
一、封装表格组件 <template><el-table :data"list" :default-sort"{ prop: date }" style"width: 100%"><template v-for"item in tableColumn"><el-table-columnv-if"item.filters":prop"item…...

数据的使用、表关系的创建、Django框架的请求生命周期流程图
目录 一、数据的增删改查 1. 用户列表的展示 2. 修改数据的逻辑分析 3. 删除功能的分析 二、如何创建表关系 三、Django的请求生命周期流程图 一、数据的增删改查 1. 用户列表的展示 把数据表中得用户数据都给查询出来展示在页面上 查询数据 def userlist(request):&qu…...

Python基础教程:类--继承和方法的重写
嗨喽,大家好呀~这里是爱看美女的茜茜呐 什么是继承 继承就是让类与类之间产生父子关系,子类可以拥有父类的静态属性和方法 继承就是可以获取到另一个类中的静态属性和普通方法(并非所有成员) 在python中,新建的类可…...
Three.js提供了多种类型的灯光
Three.js提供了多种类型的灯光,包括环境光、点光源、平行光源和聚光灯。这些灯光可以用来照亮场景中的物体,使其看起来更加真实。 环境光(AmbientLight):环境光会均匀地照亮场景中的所有物体,没有方向,不能用来投射阴…...
精通Nginx(10)-负载均衡
负载均衡就是将前端过来的负载分发到两台或多台应用服务器。Nginx支持多种协议的负载均衡,包括http(s)、TCP、UDP(关于TCP、UDP负载均衡另文讲述)等。 目录 HTTP负载均衡 负载均衡策略 轮询 least_conn(最少连接) hash(通用哈希) ip_hash(IP 哈希) random(随…...

最新SpringBoot+SpringCloud+Nacos微服务框架分享
文章目录 前言一、服务规划二、架构核心1.cloud的pom2.gateway的异常handler3.gateway的filter4、admin的pom5、admin的登录核心 三、code-helper分享总结 前言 最近有个活蛮赶的,根据Excel列的需求预估的工时直接打骨折,不要问我为什么,主要…...

[10-3]软件I2C读写MPU6050 江协科技学习笔记(16个知识点)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16...

IT供电系统绝缘监测及故障定位解决方案
随着新能源的快速发展,光伏电站、储能系统及充电设备已广泛应用于现代能源网络。在光伏领域,IT供电系统凭借其持续供电性好、安全性高等优势成为光伏首选,但在长期运行中,例如老化、潮湿、隐裂、机械损伤等问题会影响光伏板绝缘层…...

MFC 抛体运动模拟:常见问题解决与界面美化
在 MFC 中开发抛体运动模拟程序时,我们常遇到 轨迹残留、无效刷新、视觉单调、物理逻辑瑕疵 等问题。本文将针对这些痛点,详细解析原因并提供解决方案,同时兼顾界面美化,让模拟效果更专业、更高效。 问题一:历史轨迹与小球残影残留 现象 小球运动后,历史位置的 “残影”…...

[大语言模型]在个人电脑上部署ollama 并进行管理,最后配置AI程序开发助手.
ollama官网: 下载 https://ollama.com/ 安装 查看可以使用的模型 https://ollama.com/search 例如 https://ollama.com/library/deepseek-r1/tags # deepseek-r1:7bollama pull deepseek-r1:7b改token数量为409622 16384 ollama命令说明 ollama serve #:…...
SpringAI实战:ChatModel智能对话全解
一、引言:Spring AI 与 Chat Model 的核心价值 🚀 在 Java 生态中集成大模型能力,Spring AI 提供了高效的解决方案 🤖。其中 Chat Model 作为核心交互组件,通过标准化接口简化了与大语言模型(LLM࿰…...

热门Chrome扩展程序存在明文传输风险,用户隐私安全受威胁
赛门铁克威胁猎手团队最新报告披露,数款拥有数百万活跃用户的Chrome扩展程序正在通过未加密的HTTP连接静默泄露用户敏感数据,严重威胁用户隐私安全。 知名扩展程序存在明文传输风险 尽管宣称提供安全浏览、数据分析或便捷界面等功能,但SEMR…...

动态规划-1035.不相交的线-力扣(LeetCode)
一、题目解析 光看题目要求和例图,感觉这题好麻烦,直线不能相交啊,每个数字只属于一条连线啊等等,但我们结合题目所给的信息和例图的内容,这不就是最长公共子序列吗?,我们把最长公共子序列连线起…...

【版本控制】GitHub Desktop 入门教程与开源协作全流程解析
目录 0 引言1 GitHub Desktop 入门教程1.1 安装与基础配置1.2 核心功能使用指南仓库管理日常开发流程分支管理 2 GitHub 开源协作流程详解2.1 Fork & Pull Request 模型2.2 完整协作流程步骤步骤 1: Fork(创建个人副本)步骤 2: Clone(克隆…...

【工具教程】多个条形码识别用条码内容对图片重命名,批量PDF条形码识别后用条码内容批量改名,使用教程及注意事项
一、条形码识别改名使用教程 打开软件并选择处理模式:打开软件后,根据要处理的文件类型,选择 “图片识别模式” 或 “PDF 识别模式”。如果是处理包含条形码的 PDF 文件,就选择 “PDF 识别模式”;若是处理图片文件&…...