[前端基础]promise的基本使用(包括async,await语法糖)

news2025/1/19 11:35:34

目录

1.什么是promise

2.关于promise对象

3.promise常用API

4.promise链式调用

5.async和await语法糖


1.什么是promise

众所周知,在以往,我们处理异步操作主要是通过回调函数处理

setTimeout(()=>{},100);

例如这个定时器,异步操作会通过时间循环队列实现.

但是比如说我们想要执行多个嵌套的异步操作就会变的很麻烦

而且可读性非常差,代码像是很多层循环那样,向右延申,这种情况我们称之为"回调地狱"

所以我们引入了promise这种东西,来处理回调地狱问题,promise对象会把异步操作封装在then函数的不同操作中,这样就实现了链式的异步调用

举个例子,还是定时器


const p=new Promise((resolve,reject)=>{
    setTimeout(()=>{
        let n=2;
        if(n===1){
            resolve(n);   
        }else{
            reject(n);    
        }
    },10);

})
p.then((value)=>{ },(reason)=>{ });

这样可以将嵌套执行的异步操作放到后面,这就是promise的作用

2.关于promise对象

promise对象用来封装异步操作,并且可以根据then来执行嵌套的异步操作,增强额可读性

(0)promise的三种状态以及形成方法

promise有三种状态,分别是:

pending挂起状态,主要用于后面的promise链终止

fulfilled 成功状态 

rejected失败状态

这三种状态的转化关系一般是由pending状态转化为其他两种,并且在转化完成以后执行相关的回调函数,详见下方 

(1)promise的构造

promise最常用的构造方法是

const p=new Promise((resolve,reject)=>{
     ......
})

这样就会返回一个对应的promise对象,对于这个对象可以调用then方法

其中,传入的回调函数中有两个参数,这两参数我们按习惯命名为resolve还有reject,这两个函数可以在Promise调用构建的时候执行,分别代表不同的状态

resolve("成功的参数");
reject("失败的参数");

这两个函数调用以后会改变Promise对象的状态,分别会改变成为fulfilled(成功状态,也叫做resolved)和rejected(失败状态).并且在随后的then中,选择对应的函数进行执行(then中的第一个函数对应成功状态,第二个函数对应失败状态)

另外,promise也具有其他的生成方式,比如直接调用resolve和reject,会直接生成状态为fulfilled和rejected状态的Promise对象

const a=Promise.resolve(value);
const b=Promise.reject(reason);

其实每次调用then,catch的时候也会创建一个新的promise对象

const P=p.then{
   ()=>{
       /.......
   }
}

这个生成的回调函数的状态,由选定的回调函数的执行结果来确定

以下是四种常见情况

(1)当内部函数正常执行,没有返回值,则生成的promise对象为fulfilled状态.value为undefined

(2)当函数return了非promise的对象,也为fulfilled状态,这个对象即为value值

(3)当函数return了promise对象,则then生成的,就是这个promise对象,状态和value/reason都对的上

(4)当我们手动抛出错误的时候,生成rejected状态,并且reason就是throw的数值

最后补充一种promise的生成方式

const P=new Promise((resolve,reject)=>{
    //在函数内部不执行这两个函数
    //也就是不变化状态
})

这种生成方式将会生成一个pending状态的promise函数,主要用途是在后面进行promise链条中断

(2)promise生成的时候,对应的函数,数据的关系

const P = new Promise((resolve,reject)=>{
   if(符合某种条件){
      resolve(data);     1
   }else{
      reject(err);       2
   }
}).then(
   (value)=>{},          3
   (reason)=>{}          4
)

 这是我们最常用的一种方式,其中1,2函数一旦调用,会把Promise从pending对象转化为对应的状态,并且选择then中的回调函数 . 关于回调函数的选择和状态的改变谁先谁后要视情况而定,不过可以确定的一点是,回调函数的执行一定在状态确认之后

其中data会传入value,err会传入reason.但是13和24并不是同一个函数,仅仅只是参数数值一样

then中的两个参数应该被称作resolved()和rejected()

3.promise常用API

首先Promise构造函数,构造函数中的函数(resolve,reject)=>{}虽然是回调
但是是同步的

(1)resolve函数和reject函数

const a=Promise.resolve();
const b=Promise.reject();

这两个函数前面已经解释过了,这里就不加解释了

(2)util下的promisify函数

有些时候,我们想要把一个函数封装成一个promise的生成对象,比如我们可以手动把fs下的readfile函数封装成一个能返回promise对象的函数,然后再通过这个函数执行获得promise对象,再利用这个对象执行读取结束的异步操作

 大致操作就是这样子

function myReadFile(path){
    const p=new Promise((resolve, reject)=>{
        require("fs").readFile(path,(err, data)=>{
            if(err){   reject(err);}
            else{  resolve(data);  }
        })
    })
    return p;
}
myReadFile("./target").then((value)=>{console.log("结果为"+value)},(reason)=>{console.log("错误"+reason)})

再util中提供了这样一种方法promisify,可以把这类异步回调作为最后一个参数,并且回调函数的风格是先把报错放在第一个参数的样式,给转化为一个能返回promise对象的函数

 操作结果如下

var P_fun=util.promisify(require("fs").readFile);
//这个函数的返回值就是一个状态执行结束的promise对象
P_fun("./target").then((value)=>{console.log(value)});

简化很多了对吧,创建出的新的函数中,参数相比于原来,少了最后的那个回调函数

也就是说,这个P_fun函数可以是两个参数(地址,编码)也可以是一个参数(地址)//默认ascii编码

后面结合那两个语法糖,可以节省很多代码

(3)Promise.prototype.then

这是then函数的来源,这里补充点

then中可以只有一个回调函数,只能检测到变为fulfilled状态下的promise对象

p.then((value)=>{   })

then   全称应该是promise.prototype.then(不懂这个请参考js中的原型部分讲解捏)
执行完以后会返回一个新的promise对象
then只有一个参数的时候,默认这个是resolve
另外,一个pormise对象可以通过then绑定多个回调函数
只要符合当前对象状态的回调,都可以执行!

p.then(()=>{})
//第一个会执行
p.then(()=>{})
//第二个也是会执行的

then的返回也是一个promise对象,这个对象的状态由什么决定,上面已经说过了,另外根据其能返回peromise对象这件事,可以最后构成链式promise

(4)Promise.prototype.catch

P_fun("./targ","utf-8").catch((reason)=>{console.log(reason)})

区别就是catch只有一个参数,并且这个回调只能查看错误结果(也就是promise变为rejected)
而且执行完以后也会变成新的promise对象

(5)Promise.prototype.all

传入参数是一个pormise对象数组,如果数组中所有对象均为fulfilled,则新产生的promise对象为成功,value数值为所有对象的value组成的数组

var a =Promise.all([Promise.resolve(1),Promise.resolve(2)]);

 否则,对象的状态为失败,reason为第一个失败的promise对象的value

(6)Promise.prototype.race

传入的参数同样是第一个数组,返回的对象是第一个确定状态的promise对象

  var b =Promise.race([Promise.resolve(3),Promise.reject(4)]);

 
经过验证,then/catch函数产生的promise对象的状态
与指定的回调函数的执行结果有关
而与选择的哪个回调函数无关!

4.promise链式调用

链式调用的原理,是then调用以后,会根据选择的回调函数的执行情况来生成一个新的promise

举个例子

const p=new Promise((resolve, reject)=>{
    console.log("第一次执行成功");
    resolve("hhh");
})
    .then((value)=>{console.log("第二次执行成功")})
    .then((value)=>{console.log("第三次执行成功")})
    .then((value)=>{console.log("第四次执行成功")})
    

会把上述的东西都输出便,原理就是每次执行完一个promise,会根据这个对象的状态再then中选择一个合适的执行函数进行执行.

(情况1)当没有合适的执行函数的时候,会发生什么?(链式跳转的原理)

还是举个例子先

const p=new Promise((resolve, reject)=>{
    resolve("hh");
})
    .catch((reason)=>{console.log("执行失败");return Promise.reject("dd")})
    .then((value)=>{console.log(value)})

 catch不具备识别正确的promise的能力,,并且这里也无法返回正确的理论上来说不会执行?

 是会执行的,并且会正确输出 " hh "

 执行原理如下:如果then函数前面的promise对象返回的状态,在then或者catch中没有对应的回调函数可以调用,就会自动跳过这个then函数!

按照这个原理,我们常常这样子处理

.then((value)=>{console.log("第二次执行成功")})
    .then((value)=>{console.log("第三次执行成功")})//这样的话会截断到这里,然后继续执行
    .then((value)=>{console.log("第四次执行成功")})
    .catch((reason)=>{console.log("第五次执行失败")})

 在最后设置一个catch来截取最终错误的情况,如果某一步产生了错误的结果会直接忽略掉下面的部分,直接运行最后一个catch

还有一种链中断的方法也是应用了这个性质,在执行回调函数的时候返回一个pending状态的promise对象,没有任何函数能接得住这个东西,所以会跳过后面所有的部分.

这里就不演示了

5.async和await语法糖

在2017的新标准中引入了async和await语法糖,这兄弟俩.......

(1)async函数,把一个函数编程异步,并且返回一个promise对象

返回promise的规则和前面的then,catch差不多

(2)await函数,其实不应该叫做函数,是一个操作符

await右侧是一个promise对象 加上这个操作符以后,可以读到promise对象的数值(value或者reason这个参数的数值)

 let a=await myReadFile("./target","utf-8");

 console.log(a);

 输出的是读取结果,也就是data

 并且await关键字只能在async标记的函数中实现

async function main(){
           let  a= await Promise.resolve("ddd");
            console.log(a); //ddd
           let c=await Promise.reject("ddd").catch((reason)=>{return reason});
            console.log(c); //ddd
            return 1;
        }
        const p=main();
        console.log(p)

 这个东西的执行结果为

 

(3)简单的实践

结合promisify,await async简化一个读文件和写文件的操作

const util=require("util");
const myreadfile = util.promisify(readfile);
const mywritefile = util.promisify(writefile);

async function main(){
     let data=await myreadfile("./target");
     let ??=await mywritefile("./目标",data);
}

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

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

相关文章

PID控制---基于python模拟

PID是控制中非常经典的一个模型,利用P(比例)、I(微分)、D(积分)三者相互合作来实现对一个信号的闭环控制,同时可以让信号波形平滑变化而不是突变。以下主要是结合一个python实现的控制速度的程序来模拟PID的过程。 参考博客:电机控制进阶——…

SOA与中间件、基础件的发展

应运而生的SOA   美国著名的IT市场研究和顾问咨询公司Gartner预测:到2006年,采用面向服务的企业级应用将占全球销售出的所有商业应用产品的80 以上到2008年,SOA将成为绝对主流的软件工程实践方法。近几年全球各大IT巨头纷纷推出自己的面向服务的应用平…

WordPress切换为经典编辑器可视化/文本切换不显示解决方法

WordPress文章编辑器切换为经典编辑器可视化/文本状态无法切换消失的问题有用户遇到过,出现这样的问题时,我们首先应该回忆一下,之前自己进行了哪些操作导致这种情况的出现,比如: 更换了新主题? 安装了新插…

软考高项 - 计算公式汇总整理

大家好,我是陈哈哈,我准备参加23年5月份的软考信息项目管理师考试,学习之余还是习惯做一些重点、考点的记录,用于自己复习,以及分享给日后考试的考友,啥都不说了,祝通过🌹&#x1f3…

【设计模式】UML类图与软件设计原则

1,设计模式概述 1.1 软件设计模式的产生背景 "设计模式"最初并不是出现在软件设计中,而是被用于建筑领域的设计中。 1977年美国著名建筑大师、加利福尼亚大学伯克利分校环境结构中心主任克里斯托夫亚历山大(Christopher Alexand…

Docker基础学习1

Docker 1 Docker简介 1.1 什么是虚拟化 在计算机中,虚拟化(英语:Virtualization)是一种资源管理技术,是将计算机的各种实体资源,如服务器、网络、内存及存储等,予以抽象、转换后呈现出来&…

数字温度传感器-DS18B20

文章目录 一、DS18B20器件图二、DS18B20特点三、DS18B20内部结构内部构成 四、工作时序1.初始化时序2.ReadOneChar2.WriteOneChar 一、DS18B20器件图 DS18B20的管脚排列: GND为电源地;DQ为数字信号输入/输出端;VDD为外接供电电源…

【车载助手形象系列二】---车载助手数智人形象制作可以使用AI绘图替代?

上期分享了《车载助手形象系列:数智人入门级介绍》,本期主要讲解下数智人形象制作过程 数字人形象 数智人覆盖了卡通、二次元、写实等多元化风格的虚拟形象,可高精度还原表情和躯干动作,提供涵盖人物设计、3D制作、内容制作、IP运…

深度学习之torchvision、多层感知器与激活函数

文章目录 1 torchvision库与加载内置图片数据集2 多层感知器3 激活函数3.1 ReLU激活函数3.2 Sigmoid激活函数3.3 Tanh激活函数3.4 LeakyReLU激活函数 学习笔记 1 torchvision库与加载内置图片数据集 torchvision库是PyTorch中用来处理图像和视频的一个辅助库,提供了…

Docker部署文本语义检索系统

Docker 部署 pipelines服务 需求:基于docker部署百度飞桨公开项目pipelines服务之 端到端文本语义检索系统 项目链接:https://aistudio.baidu.com/aistudio/projectdetail/3351784?channelType0&channel0 代码地址:https://gitee.com/pa…

03 Kubernetes 系统快速入门

课件 Kubernetes 中的 ownerReference 字段用于建立资源之间的所有者关系,即父子关系。这个字段可以用来指定一个资源是另一个资源的所有者。例如,一个 Deployment 可以拥有多个 Pod,这些 Pod 就可以通过 ownerReference 字段引用到对应的 De…

D. Maximum Distance(最小生成树)

Problem - D - Codeforces Chouti已经厌倦了乏味的作业,于是他打开了数年前创建的一个旧编程问题。 给定一个具有n个节点和m条加权边的连通无向图。其中有k个特殊节点:x1,x2,...,xk。 现在定义路径的成本为其边权的最大值。两个顶点之间的距离定义为连…

从C出发 29 --- 指针与函数

只有知道具体位置,才有可能跳到那个位置去执行,如果不知道在哪里,怎么跳? 函数是什么? 函数就是一片连续的内存 数组是什么? 数组就是一片连续的内存 很显然,这一片连续的内存当中&#xff0c…

百度智能云六大产品系列将率先基于大模型升级,加速全面智能化

百度智能云在上海召开文心一言技术交流会。百度集团副总裁袁佛玉表示,大模型的技术突破,使得全球性的“AI再造”已经拉开序幕。在技术底座层面,百度智能云拥有全栈自研的AI大底座;在大模型平台层面,拥有全球首个一站式…

OSGI详解

最近项目用到了OSGI,第一反应就是什么是OSGI? OSGI是一个为Java提供动态模块化的系统; 准确的说,OSGI是一个标准,一个框架,也可以理解为一个容器,具体的实现有Eclipse下的Equinox和Appach下的Fe…

18. Unity - 2D游戏开发小记01 --- 瓦片地图搭建基本操作

1. 瓦片地图创建(Tilemap) 在2D游戏开发中,瓦片地图就是游戏中的场景或者说游戏背景,可以使用把图片资源制作成Tiles,然后在游戏场景中搭建2D游戏的背景即可。 效果展示: 规则瓦片制作 首先在 Hierarchy层级窗口中右键鼠标,依次选择 2D Object → Tilemap → Rectan…

SpirngMVC的创建和使用

SpirngMVC的创建 SpringMVC的创建很容易。 步骤:创建SpringBoot项目,并在添加依赖时,添加Spring Web。 SpringMVC的创建就完成了。 创建SpringBoot项目详情:SpringBoot项目创建和使用_追梦不止~的博客-CSDN博客 S…

lua元表、元方法

lua元表、元方法 lua官方参考手册:https://www.runoob.com/manual/lua53doc/manual.html#2.4 一、总结: ☺ 1、普通的表,找不到了,或者无法进行运算的时候,考虑设置到它身上的元表的元方法 2、元表的本质&#xff1a…

JavaWeb03(域对象EL表达式JSTL标签)

目录 一.jsp内置对象之域对象 1.1 什么是jsp的内置对象? JSP的内置对象是指在JSP页面系统中已经默认内置的Java对象,这些对象不需要开发人员显式声明即可使用。一共有9个: 分别为request、response、session、application、out、pageContext、confi…

netfilter filter表(三)

修改《netfilter filter表(二)》的hello_open函数,将ipt_entry的信息打印处理,代码如下: char* get_verdict(int verdict) {verdict -(verdict 1);char* p "";switch (verdict){case NF_DROP:p "NF_DROP";break;cas…