回调地狱 与 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中的一个原生对象,是一种异步编程的解决方案,可以替换掉传统的回调函数解决方案。

  1. Promise 对象有三个状态:pending(进行中),fulfilled(已成功),rejected(已失败)
  2. Promise 构造函数接收一个函数作为参数,该函数是同步的并且会被立即执行,所以我们称之为起始函数,我们需要处理的异步任务就卸载在该函数体内,该函数的两个参数是resolvereject。异步任务执行成功时调用resolve函数并传递成功的结果,反之调用reject并传递失败的原因。
  3. 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 链式编程可以保证代码的执行顺序,前提是每一次在then做完处理后,一定要 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.mzph.cn/news/677160.shtml

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

相关文章

除夕日的每日一题(字符个数统计,多数元素)

字符个数统计_牛客题霸_牛客网 (nowcoder.com) #include <stdio.h> #include <string.h> #include <stdlib.h>int num0,len,i,j,k,asc; int tmp[128]{0}; char str[400]; int main() {gets(str);//gets(str) 用于从标准输入读取一个字符串并存储在 str 中len…

梯度提升树系列7——深入理解GBDT的参数调优

目录 写在开头1. GBDT的关键参数解析1.1 学习率(learning rate)1.2 树的数量(n_estimators)1.3 树的最大深度(max_depth)1.4 叶子节点的最小样本数(min_samples_leaf)1.5 特征选择的比例(max_features)1.6 最小分裂所需的样本数(min_samples_split)1.7 子采样比例(…

算法笔记P67

#include <cstdio>void swap(int*a,int*b){int temp *a;*a *b;*b temp; }int main() {int a1,b2;int *p1 &a,*p2 &b;swap(p1,p2);printf("a %d,b %d",a,b);return 0; }PS&#xff1a;对*和&的理解 1. * &#xff1a;例如int* p的理解&#…

AWD-Test2

1.已知账号密码&#xff0c;可SSH连接进行代码审计。2.登录可万能密码进入&#xff0c;也可注册后登录。3.修改url参数&#xff0c;发现报错。确定为Linux系统4.写入一句话&#xff0c;并提交。&#xff08;也可以文件上传&#xff0c;这里采用简洁的方法&#xff09; <?p…

修改SpringBoot中默认依赖版本

例如SpringBoot2.7.2中ElasticSearch版本是7.17.4 我希望把它变成7.6.1

电商商城系统网站

文章目录 电商商城系统网站一、项目演示二、项目介绍三、系统部分功能截图四、部分代码展示五、底部获取项目&#xff08;9.9&#xffe5;带走&#xff09; 电商商城系统网站 一、项目演示 商城系统 二、项目介绍 基于SpringBootVue的前后端分离商城系统网站 运行环境:idea或…

【开源计算机视觉库OpencV详解——超详细】

开源计算机视觉库OpencV详解 1. 介绍2. 核心功能3. 安装OpenCV4. 示例&#xff1a;使用Python读取和显示图像5. 示例&#xff1a;使用Python捕捉视频6. 获取帮助和文档 1. 介绍 OpenCV&#xff08;Open Source Computer Vision Library&#xff09;是一个开放源码的计算机视觉…

M1 Mac使用SquareLine-Studio进行LVGL开发

背景 使用Gui-Guider开发遇到一些问题,比如组件不全。使用LVGL官方的设计软件开发 延续上一篇使用的基本环境。 LVGL项目 新建项目 选择Arduino的项目,设定好分辨率及颜色。 设计UI 导出代码 Export -> Create Template Project 导出文件如图 将libraries下的ui文…

【原理图PCB专题】Cadence17.4 PCB位号重排与反标

在文章:【原理图专题】Cadence 16.6如何把PCB元件位号重排并反标到原理图 中我们讲到了Cadence16.6版本对原理图进行反标的操作。 对于反标之前我们是通过如下所示的绘制流程来讲的,一般在首板或是大改板操作器件里有很多不同的很大的位号,这时我们可以通过Backannotate功能…

C++初阶篇----新手进村

目录 一、什么是C二、C关键字三、命名空间3.1命名空间的定义3.2命名空间的使用 四、C输入和输出五、缺省参数5.1缺省参数的概念5.2缺省参数的分类 六、函数重载6.1函数重载的概念6.2函数重载的原理----名字修饰 七、引用7.1引用概念7.2引用特性7.3常引用7.4引用的使用7.5传值、…

如何使用python网络爬虫批量获取公共资源数据实践技术应用

要使用Python网络爬虫批量获取公共资源数据&#xff0c;你需要遵循以下步骤&#xff1a; 确定目标网站和数据结构&#xff1a;首先&#xff0c;你需要明确你要爬取的网站以及该网站的数据结构。了解目标网站的数据结构和API&#xff08;如果有的话&#xff09;是关键。选择合适…

考研高数(导数的定义)

总结&#xff1a; 导数的本质就是极限。 函数在某点可导就必连续&#xff0c;连续就有极限且等于该点的函数值。 例题1&#xff1a;&#xff08;归结原则的条件是函数可导&#xff09; 例题2&#xff1a; 例题3&#xff1a;

使用 Elasticsearch 和 OpenAI 构建生成式 AI 应用程序

本笔记本演示了如何&#xff1a; 将 OpenAI Wikipedia 向量数据集索引到 Elasticsearch 中使用 Streamlit 构建一个简单的 Gen AI 应用程序&#xff0c;该应用程序使用 Elasticsearch 检索上下文并使用 OpenAI 制定答案 安装 安装 Elasticsearch 及 Kibana 如果你还没有安装好…

Python爬虫Xpath库详解#4

爬虫专栏&#xff1a;http://t.csdnimg.cn/WfCSx 前言 前面&#xff0c;我们实现了一个最基本的爬虫&#xff0c;但提取页面信息时使用的是正则表达式&#xff0c;这还是比较烦琐&#xff0c;而且万一有地方写错了&#xff0c;可能导致匹配失败&#xff0c;所以使用正则表达式…

1275.找出井字棋的获胜者(Java)

题目描述&#xff1a; A 和 B 在一个 3 x 3 的网格上玩井字棋。 井字棋游戏的规则如下&#xff1a; 玩家轮流将棋子放在空方格 (" ") 上。 第一个玩家 A 总是用 “X” 作为棋子&#xff0c;而第二个玩家 B 总是用 “O” 作为棋子。 “X” 和 “O” 只能放在空方格中&…

FPGA_简单工程_状态机

一 理论 fpga是并行执行的&#xff0c;当处理需要顺序解决的事时&#xff0c;就要引入状态机。 状态机&#xff1a; 简写FSM&#xff0c;也称同步有限状态机。 分为&#xff1a;more型状态机&#xff0c;mealy型状态机。 功能&#xff1a;执行该事件&#xff0c;然后跳转到下…

相机图像质量研究(11)常见问题总结:光学结构对成像的影响--像差

系列文章目录 相机图像质量研究(1)Camera成像流程介绍 相机图像质量研究(2)ISP专用平台调优介绍 相机图像质量研究(3)图像质量测试介绍 相机图像质量研究(4)常见问题总结&#xff1a;光学结构对成像的影响--焦距 相机图像质量研究(5)常见问题总结&#xff1a;光学结构对成…

Linux 36.2@Jetson Orin Nano之Hello AI World!

Linux 36.2Jetson Orin Nano之Hello AI World&#xff01; 1. 源由2. Hello AI World&#xff01;3. 步骤3.1 准备阶段3.2 获取代码3.3 Python环境3.4 重点环节3.5 软件配置3.6 PyTorch安装3.7 编译链接3.8 安装更新 4. 测试4.1 video-viewer4.2 detectnet4.3 演示命令 5. 参考…

【OrangePi Zero2 智能家居】阿里云人脸识别方案

一、接入阿里云 二、C语言调用阿里云人脸识别接口 三、System V消息队列和POSIX 消息队列 一、接入阿里云 在之前树莓派的人脸识别方案采用了翔云平台的方案去1V1上传比对两张人脸比对&#xff0c;这种方案是可行&#xff0c;可 以继续采用。但为了接触更多了云平台方案&…

互联网摸鱼日报(2024-02-09)

互联网摸鱼日报(2024-02-09) 博客园新闻 开启 LLMs 应用开发之门 | 新程序员 技术宅死磕云游戏&#xff0c;米哈游的10亿玩家梦 苹果发布开源 AI 模型 MGIE 全新宝马 5 系旅行车发布 iPhone 16 最新相机模组设计&#xff1a;垂直排列&#xff0c;凸起呈药丸形状 马斯克评…