Promise难懂?一篇文章让你轻松驾驭

✅ 作者简介:一名普通本科大三的学生,致力于提高前端开发能力
✨ 个人主页:前端小白在前进的主页
🔥 系列专栏 : node.js学习专栏
⭐️ 个人社区 : 个人交流社区
🍀 学习格言: ☀️ 打不倒你的会使你更强!☀️


🔥前言

前端js学习中,让大家最难受的就是异步的问题,解决异步、回调地狱等问题时你必须得学会promise,对于多数前端程序员来说promise简直就是噩梦,本篇文章就是从通俗易懂的角度做为切入点,帮助大家轻松掌握promise

在这里插入图片描述


📃目录

  • 异步编程
  • 回调函数
  • 回调地狱
  • Promise
    • Promise语法格式
    • Promise链式
    • Promise.all()
  • 小结

异步编程

想要学习promise,你必须要懂得什么是异步编程!众所周知,js语言是单线程机制。所谓单线程就是按次序执行,执行完一个任务再执行下一个。但是不影响存在同步异步的两种操作,这两种操作做事情其实都是在一条流水线上(单线程),只是这两种操作在单线程上的执行顺序不一样罢了!当js触发到异步任务时,会将异步任务交给浏览器处理,当执行有结果时,会把异步任务的回调函数插入待处理队列的队尾!

我们通俗的去解释一下我们的异步:异步就是从主线程发射一个子线程来完成任务,每一个任务有一个或多个回调函数(callback),前一个任务结束后,不是执行后一个任务,而是执行回调函数后一个任务则是不等前一个任务结束就执行,所以程序的执行顺序与任务的排列顺序是不一致的、异步的.

在这里插入图片描述

该图摘自于菜鸟教程中的异步编程小节,帮助大家更好的理解什么是异步!


回调函数

回调函数的定义非常简单:一个函数被当做一个实参传入到另一个函数(外部函数),并且这个函数在外部函数内被调用,用来完成某些任务的函数。就称为回调函数

回调函数的两种写法(实现效果相同):

const text = () => {document.write('hello james')
}
setTimeout(text,1000)
setTimeout(()=>{document.write("hello james")
},1000)

这段代码中的 setTimeout 就是一个消耗时间较长的过程,它的第一个参数是个回调函数,第二个参数是毫秒数,这个函数执行之后会产生一个子线程,子线程会等待 1 秒,然后执行回调函数 "text",在文本中输出hello james

setTimeout会在子线程中等待1秒,但是主线程的运行不会受到影响!例如以下代码:

setTimeout(()=>{document.write("hello davis")
},1000)
console.log('123456');

在这里会先打印出来123456(主线程),然后一秒后在文本中输出hello davis(子线程)


回调地狱

回调地狱这个词听起来就非常的高大上,想要接触Promise之前,必须要懂得什么是回调地狱,以及为什么会产生回调地狱?
先来看看概念:当一个回调函数嵌套一个回调函数的时候就会出现一个嵌套结构当嵌套的多了就会出现回调地狱的情况
举个例子
比如我们发送三个 ajax 请求:

  • 第一个正常发送
  • 第二个请求需要第一个请求的结果中的某一个值作为参数
  • 第三个请求需要第二个请求的结果中的某一个值作为参数

你会看到以下代码

$.ajax({url: '我是第一个请求',type: 'get',success (res) {// 现在发送第二个请求$.ajax({url: '我是第二个请求'type:'post',data: { a: res.a, b: res.b },success (res1) {// 进行第三个请求$.ajax({url: '我是第三个请求',type:'post',data: { a: res1.a, b: res1.b },success (res2) { console.log(res2) }})}})}
})

这种代码看起来属实是折磨人啊!当我们把代码写成这样的时候,就陷入了可维护性差的状态了,代码体验非常的不良好,看一会就给看懵了,为了解决这个问题,于是,就引入了我们的Promise,用Promise去解决回调地狱问题!


Promise

Promise异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大,它是一个 ECMAScript 6 提供的类,目的是更加优雅地书写复杂的异步任务

Promise对象有以下两个特点:

  1. 对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是Promise这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。
  2. 一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。如果改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。

两个特点摘自于😛😛阮一峰ES6文章😛😛


Promise语法格式

new Promise(function (resolve, reject) {// resolve 表示成功的回调// reject 表示失败的回调
}).then(function (res) {// 成功的函数
}).catch(function (err) {// 失败的函数
})

出现了new关键字,就明白了Promise对象其实就是一个构造函数,是用来生成Promise实例的。能看出来构造函数接收了一个函数作为参数,该函数就是Promise构造函数的回调函数,该函数中有两个参数resolvereject,这两个参数也分别是两个函数!

简单的去理解的话resolve函数的目的是将Promise对象状态变成成功状态,在异步操作成功时调用,将异步操作的结果,作为参数传递出去。reject函数的目的是将Promise对象的状态变成失败状态,在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。

Promise实例生成以后,可以用then方法分别指定resolved状态rejected状态的回调函数。

代码示例:

        const promise = new Promise((resolve,reject)=>{//异步代码setTimeout(()=>{// resolve(['111','222','333'])reject('error')},2000)})promise.then((res)=>{//兑现承诺,这个函数被执行console.log('success',res);}).catch((err)=>{//拒绝承诺,这个函数就会被执行console.log('fail',err);})

代码分析:

上边说到Promise是一个构造函数,new之后等于说调用了构造函数,构造函数中传的参数是一个函数,这个函数内的两个参数分别又是两个函数(reslovereject),虽然感觉很绕,但是理清思路会很清晰的!


我们得到对象promise,promise对象中自带有两个方法thencatch,这两个方法中会分别再传入一个回调函数,这个回调函数的目的在于返回你所需要成功或失败的信息!那么怎么去调用这两个回调函数呢?
看下方图可以快速理解:

在这里插入图片描述
这两个函数分别做为参数(reslovereject)传到上方的函数中去了.随后在构造函数的回调函数中写入异步代码(例如:ajax定时器),这里使用了定时器作为例子,如果你想表达的是成功回调,你可以在内部调用函数reslove('一般情况下是后端返回的成功数据)。如果你想表达的是失败回调,你可以调用reject('一般情况下是后端返回的失败信息').

这些就是Promise执行的过程!虽然理解着很绕,但是多读几遍绝对有不一样的收获!


Promise链式

then方法返回的是一个新的Promise实例注意:不是原来那个Promise实例)。因此可以采用链式写法,即then方法后面再调用另一个then方法
实际案例:
我想要实现在一个数组中查看一个帖子,但是我最终的目的是得到这个帖子下面的所有评论,这该怎么实现呢?
实现思路
先从一个接口中获取这个帖子的信息,然后通过该帖子的帖子id从而获取到该帖子下的所有评论
代码如下:

pajax({url:"http://localhost:3000/news",data : {author : "james"}
}).then(res=>{return pajax({url : "http://localhost:3000/comments",data : {newsId : res[0].id}})
}).then(res=>{console.log(res);
}).catch(err=>{console.log(err);
})

代码分析:

这里使用了一个Promise已经封装过的ajax,我们从第一个接口中得到了帖子id,然后在then中的函数发送第二个请求(携带了第一个请求返回过来的参数),我们最后想要拿到第二个接口的结果,于是又有了一个then方法,但是在第一个then方法中要把一个新的Promise实例return出去,这样的话,第二个then才起作用!(这是因为then方法是Promise 实例所具有的方法,也就是说,then方法是定义在原型对象Promise.prototype上的)====>我们可以打印一下:console.log(Promise.prototype)

在这里插入图片描述
可以看的出来原型对象Promise.prototype中是有then方法的!


Promise.all()

Promise.all()方法用于将多个 Promise 实例,包装成一个新的 Promise 实例

语法格式:

const p = Promise.all([p1, p2, p3]);

Promise.all()方法接受一个数组作为参数,p1、p2、p3都是 Promise 实例,如果不是,就会调用Promise.reslove() [该方法可自行了解]自动将参数转为 Promise 实例,再进一步处理。
说那么多白话没用,我们可以根据一个案例,就可以明白Promise.all()的用途了。
实际案例:
如果你想实现一个效果:在一个页面中,等到页面中所有的请求返回数据后,再渲染页面,该怎么实现呢?(在实际开发中我们会看到loading加载页面,等数据返回完后,loading加载页面会消失,整个页面就展现出来了,增强用户的体验。)
实现思路:
通过Promise.all()方法,等多个接口全部接收到数据后,再统一进行处理,然后渲染页面
代码如下:

console.log("显示加载中")
const q1 = pajax({url:"http://localhost:3000/looplist"
})const q2 = pajax({url:"http://localhost:3000/datalist"
})
Promise.all([q1,q2]).then(res=>{console.log(res)console.log("隐藏加载中...")
}).catch(err=>{console.log(err)
})

代码分析:

在上方代码中,全局打印显示加载中是代替loading的页面,表示该页面现在正是loading页面中,等到q1q2所请求接口都得到返回的信息后,在then方法中接收收据,并且可以进行渲染页面,同时隐藏了loading加载页面!

小结

不论是在前端的项目开发中还是在前端的面试过程中,Promise的地位就是举足轻重的,虽然解决异步编程的终极解决方案是async和await,但是它们也是基于Promise封装而来的,在以往文章中,我就说过,学习编程重要的是搞懂某个技术是怎么实现的,而不是做一个cv侠,多去思考,才能进步。继续加油吧,少年!😀😀


👑书写不易,希望大家能够给予三连支持,期待我更好的文章哟👑

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

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

相关文章

Vue框架学习记录之环境安装与第一个Vue项目

Node.js的安装与配置 首先是Node.js的安装,安装十分简单,只需要去官网下载安装包后,一路next即可。 Node.js是一个开源的、跨平台的 JavaScript 运行时环境 下载地址,有两个版本,一个是推荐的,一个是最新…

PostgreSQL PG15 新功能 PG_WALINSPECT

开头还是介绍一下群,如果感兴趣PolarDB ,MongoDB ,MySQL ,PostgreSQL ,Redis ,Oracle ,Oceanbase 等有问题,有需求都可以加群群内有各大数据库行业大咖,CTO,可以解决你的问题。加群请加微信号 liuaustin3 (…

容器资料: Docker和Singularity

容器资料 Docker和Singularity Docker比较适合测试: 环境适配,每种环境对应一个容器。Docker需要host宿主机上运行Docker服务(root权限),隔离性很高,但会牺牲性能,对GPU环境支持不好(需要安装NVIDIAN公司的插件才能把GPU暴露给container) Sigularity可…

实时测试工具 Visual Studio 扩展 NCrunch 4.18 Crack

NCrunch Visual Studio 扩展 .NET 的终极实时测试工具 在编码时查看实时测试结果和内联指标。 下载v4.18 发布于 2023 年 7 月 17 日 跳过视频至: 代码覆盖率 指标 分布式处理 配置 发动机模式 Visual Studio 自动并发测试 NCrunch 是一个完全自动化的测试扩展&a…

stc单片机外部中断+EC11编码器实现计数功能

stc单片机外部中断+EC11编码器实现计数功能 🎬 串口输出测试效果: 📑EC11编码器原理图: 🍁EC11编码器输出信号说明: 📗检测说明: 📑以EC11-A信号作为一个时钟基准信号,检测到EC11-A之后,再去判断B的动作,一个相对的电平。当检测到A信号下降沿触发后,检测…

Linux系统中驱动面试分享

​ 1、驱动程序分为几类? 字符设备驱动 块设备驱动 网络设备驱动 2、字符设备驱动需要实现的接口通常有哪些 open、close、read、write、ioctl等接口。 3、主设备号与次设备号的作用 主设备号和次设备号是用来标识系统中的设备的,主设备号用来标识…

高级IO---五种IO模型多路转接之Select

文章目录 五种IO模型1、阻塞IO2、非阻塞IO3、信号驱动IO4、多路转接IO5、异步IO总结IO 同步与异步阻塞与非阻塞设置非阻塞利用fcntl接口实现一个设置非阻塞的函数 多路转接之Selectselect函数原型fd_set结构返回值 socket就绪条件读就绪写就绪 select的特点select使用示例Util.…

转载: 又拍云【PrismCDN 】低延时的P2P HLS直播技术实践

低延时的P2P HLS直播技术实践本文是第二部分《PrismCDN 网络的架构解析,以及低延迟、低成本的奥秘》低延时的P2P HLS直播技术实践 [首页 > Open Talk NO.41 | 2018 音视频技术沙龙深圳站 > 低延时 WebP2P 直播技术实践https://opentalk-blog.b0.upaiyun.com/prod/2018-0…

TSINGSEE青犀AI视频分析/边缘计算/AI算法·人脸识别功能——多场景高效运用

旭帆科技AI智能分析网关可提供海量算法供应,涵盖目标监测、分析、抓拍、动作分析、AI识别等,可应用于各行各业的视觉场景中。同时针对小众化场景可快速定制AI算法,主动适配大厂近百款芯片,打通云/边/端灵活部署,算法一…

【Linux】高级IO --- Reactor网络IO设计模式

人其实很难抵制诱惑,人只能远离诱惑,所以千万不要高看自己的定力。 文章目录 一、LT和ET模式1.理解LT和ET的工作原理2.通过代码来观察LT和ET工作模式的不同3.ET模式高效的原因(fd必须是非阻塞的)4.LT和ET模式使用时的读取方式 二…

Java实践-物联网loT入门-MQTT传输协议

前言 MQTT是一个极其轻量级的发布/订阅消息传输协议,适用于网络带宽较低的场合. 它通过一个代理服务器(broker),任何一个客户端(client)都可以订阅或者发布某个主题的消息,然后订阅了该主题的客户端则会收…

华硕ROG2/ROG5/ROG6/ROG7Pro强解锁L锁-快速实现root权限-支持Zenfone9/8/7

2023年9月新增解锁BL适配(需要联系技术远程操作): 新增支持华硕ROG5/5S/5Pro机型强制解锁BL,并且支持OTA在线更新功能 新增支持华硕ROG6/6Pro机型强制解锁BL,并且支持OTA在线更新功能 新增支持华硕ROG7/7Pro机型强制解…

kuiper安装

1:使用docker方式安装 docker pull lfedge/ekuiper:latest docker run -p 9081:9081 -d --name kuiper -e MQTT_SOURCE__DEFAULT__SERVERtcp://127.0.0.1:1883 lfedge/ekuiper:latest这样就安装好了,但是操作只能通过命令完成,如果想要通过页面来操作&…

1065 A+B and C (64bit)

题&#xff1a;点我 题目大意&#xff1a; 这题虽然看着像签到&#xff0c;然鹅签不过去。 因为我最初写的沙雕代码是&#xff1a; #include<iostream> #include<cstdio> using namespace std; int main(void) {int t;scanf("%d", &t);for (int i …

Java后端开发面试题——JVM虚拟机篇

目录 什么是程序计数器&#xff1f; 你能给我详细的介绍Java堆吗? 什么是虚拟机栈 1. 垃圾回收是否涉及栈内存&#xff1f; 2. 栈内存分配越大越好吗&#xff1f; 3. 方法内的局部变量是否线程安全&#xff1f; 4.什么情况下会导致栈内存溢出&#xff1f; 5.堆栈的区别…

React Hook之useContext

1. 什么是useContext React官方解释&#xff1a;useContext 是一个 React Hook&#xff0c;可以让你读取和订阅组件中的 context&#xff08;React官方文档地址&#xff09;。 通俗的讲&#xff0c;useContext的作用就是&#xff1a;实现组件间的状态共享&#xff0c;主要应用场…

RFID溯源驱动汽车座椅制造的智能时代

在今天的快速发展的制造业中&#xff0c;信息化和智能化已经成为不可或缺的部分。信息化和智能化能够极大地提高生产效率、减少浪费&#xff0c;降低成本&#xff0c;提升产品的质量。汽车座椅产线信息化和智能化是汽车座椅产线升级的重要方向&#xff0c;RFID技术方案在汽车座…

【Flask】from flask_sqlalchemy import SQLAlchemy报错

【可能出现的情况】 1、未安装 Flask-SQLAlchemy&#xff1a; 在使用 flask_sqlalchemy 之前&#xff0c;你需要确保已经通过 pip 安装了 Flask-SQLAlchemy。可以通过以下命令安装它&#xff1a; pip install Flask-SQLAlchemy 2、包名大小写问题&#xff1a; Python 是区分大…

VGG 07

一、发展 1989年&#xff0c;Yann LeCun提出了一种用反向传导进行更新的卷积神经网络&#xff0c;称为LeNet。 1998年&#xff0c;Yann LeCun提出了一种用反向传导进行更新的卷积神经网络&#xff0c;称为LeNet-5 AlexNet是2012年ISLVRC 2012&#xff08;ImageNet Large Sca…

l8-d8 TCP并发实现

一、TCP多进程并发 1.地址快速重用 先退出服务端&#xff0c;后退出客户端&#xff0c;则服务端会出现以下错误&#xff1a; 地址仍在使用中 解决方法&#xff1a; /*地址快速重用*/ int flag1,len sizeof (int); if ( setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &a…