JavaScript中Promise的简单使用及其原理
Promise是ES6最重要的特性之一,今天来系统且细致的研究一下Promise的用法以及原理。
按照我往常的理解,Promise是一个构造函数,有all、resolve、reject、then、catch等几个方法,一般情况下,在涉及到异步操作时才会用到Promise。
所以我接下来先new一个Promise对象,并在其中进行一些异步操作:
// 使用Promise的时候一般会把它包裹在一个函数中,并在函数的最后返回这个Promise对象
function runPro()(var a = new Promise((resolve, reject) => {setTimeout(() => {console.log('work done!');resolve('success');}, 1000);});return a;
)
runPro()
在上面的代码中,Promise的构造函数接收一个箭头函数作为参数,这个箭头函数又有两个参数,分别是resolve和reject,我在这个箭头函数中使用setTimeout进行了一些异步操作,异步操作中执行了resolve方法,并给resolve方法传了一个字符串‘success’作为参数。
执行这段代码会发现,等待了1秒钟后(因为我在setTimeout中设置的等待时间是1000毫秒),输出了‘work done!’。
这时候并没有发现Promise有什么特别的作用,而且resolve和reject这两个的作用也并没有体现出来。
之前我们说过Promise这个构造函数上有then、catch方法,在上面的代码片段中,runPro函数最后return了一个Promise对象,所以我们可以在runPro函数执行完成之后使用then对Promise对象进行进一步的操作:
runPro().then((res) => {console.log('then:', res);//TODO something
});
输出结果:
在runPro返回的Promise对象上直接调用then方法,then方法接收一个函数作为参数A,并且这个箭头函数也会接收一个参数B,这个参数B的值就是前面代码中resolve方法所传递的字符串‘success’。
执行代码,会在1秒后首先输出‘work done!’,紧接着输出‘then: success’。
这个时候,就可以简单的体现出来Promise的作用了,在前面的代码中,then方法就像是Promise的回调函数,当Promise中的异步操作执行完之后,通过链式调用的方式执行回调函数。
这里的关键点就在于链式调用上,当实际使用中遇见多层回调的情况时,Promise的强大之处才能够体现出来:
function runPro2(){var a = new Promise((resolve, reject) => {setTimeout(() => {resolve('success');console.log('this is runPro2');}, 1000);});return a;
};function runPro3(){var a = new Promise((resolve, reject) => {setTimeout(() => {resolve('success');console.log('this is runPro3');}, 1000);});return a;
};runPro().then(() => {return runPro2();
}).then(() => {return runPro3();
}).then(() => {console.log('all done')
})
到这里为止,大概已经明白了,Promise是一个在异步操作过程中,等待其中异步操作完成之后执行其回调函数的一种结构体。但是其中的原理还是模糊不清,其中resolve
和reject
这两个参数还没有搞清楚,只知道在前面的几个代码片段中都调用了resolve函数,resolve是做什么的并没有体现出来。
关于resolve和reject,我以前的理解是Promise中的异步操作执行成功后调用resolve函数,异步操作执行失败后调用reject函数,后来发现这种理解其实是不准确的。
在理解这两个函数的正确作用之前,我们首先要知道Promise一个重要的特性:状态
Promise的状态:
一个Promise对象的当前状态必须为以下三种状态中的一种:等待(Pending)、完成(Fulfilled)、拒绝(Rejected)。
- Pending:
异步操作完成之前,Promise处于等待状态,这时候的Promise可以迁移至Fulfilled或者Rejected。 - Fulfilled:
异步操作完成之后,Promise可能从Pending状态迁移至Fulfilled状态,Fulfilled状态的Promise必须拥有一个不可变的终值,并且Fulfilled状态的Promise不能迁移为其他状态。 - Rejected:
异步操作完成之后,Promise可能从Pending状态迁移至Rejected状态,Rejected状态的Promise必须拥有一个不可变的拒绝原因,并且Rejected状态的Promise不能迁移为其他状态。
了解了Promise的三种状态之后,我们再来说说resolve
和reject
这两个函数的作用:
- resolve函数将Promise设置为Fulfilled状态,reject函数将Promise设置为Rejected状态。
- 设置为Fulfilled或者rejected状态后,即表示Promise中的异步操作执行完成,这时程序就会执行then回调函数。
- resolve和reject函数传递的参数,将由then函数中的箭头函数接收。
实际上,理解Promise的关键点就在于这个状态,通过维护状态、传递状态的方法来进行及时的回调。
所以,如下面代码所示,当使用Promise进行异步操作的时候,其中有几个关键点需要特别注意:
- 在一个函数中new了一个Promise对象之后,函数的最后必须把这个Promise对象return出来,否则这个函数就无法使用then函数进行回调;
- 异步操作中必须执行resolve或者reject函数,否则这个Promise一直处于Pending状态,代码就不会执行它的回调函数。
function runPro(){var a = new Promise((resolve, reject) => {setTimeout(() => {resolve('resolve');console.log('this is runPro');}, 1000);});return a;
}runPro().then((res) => {console.log(res);
})
同样的道理,当异步操作执行失败时,代码通过执行reject函数的方式,将Promise的状态设置为Rejected,并返回一个拒绝原因:
function runPro(item){var a = new Promise((resolve, reject) => {setTimeout(() => {if(item >= 18) {console.log('item 大于 18');resolve('一切正常!');}else {console.log('item 小于 18');reject('18+电影不允许放映!');}}, 1000);});return a;
}runPro(13)
.then((res) => {console.log('resolve:',res);
},(rej) => {console.log('reject:', rej);
})
我们给runPro函数传递不同的参数,runPro接受参数后进行一个异步的判断,如果这个参数的值小于18,执行reject函数,反之则执行resolve函数,异步操作完成之后,执行then回调函数,这里的回调函数可以接收两个箭头函数作为参数,分别对应了resolve函数的回调和reject函数的回调,这两个箭头函数可以分别拿到resolve和reject传递的参数。
如下面截图所示,分别给runPro传递两个不同值后,得到了两种不同的结果:
catch函数的用法
在Promise中,catch函数可以替代reject函数使用,用来指定接收reject的回调:
function runPro(item){var a = new Promise((resolve, reject) => {setTimeout(() => {if(item >= 18) {console.log('item 大于 18');resolve('一切正常!');}else {console.log('item 小于 18');reject('18+电影不允许放映!');}}, 1000);});return a;
}runPro(13)
.then((res) => {console.log('resolve:',res);
})
.catch((rej) => {console.log('catch:', rej);
})
如上面代码所示,对调函数then只有一个箭头函数作为参数,这种情况下,这个箭头函数就被指定用来接收resolve函数的回调,而reject函数的回调则被catch函数来接收:
这个地方使用catch函数来接收reject的回调有一个优点,当前面的then回调函数中出现位置错误时,catch函数可以对错误信息进行处理,而不会导致代码报错。这个原理和常用的try/catch语句相同。
function runPro(item){var a = new Promise((resolve, reject) => {setTimeout(() => {if(item >= 18) {console.log('item 大于 18');resolve('一切正常!');}else {console.log('item 小于 18');reject('18+电影不允许放映!');}}, 1000);});return a;
}
runPro(19)
.then((res) => {console.log(adc); // 这里的adc是一个未定义的变量,当代码执行到这里时,会抛出Error信息导致代码卡死console.log('resolve:',res);
}, (rej) => {console.log('reject:',rej);
});
runPro(19)
.then((res) => {console.log(abc); // 这里的abc是一个未定义的变量,但是由于后边使用.catch函数进行了异常捕获,所以程序不会报错。而且错误原因也会作为参数传递到后面.catch函数的参数中console.log('resolve:',res);
})
.catch((rej) => {console.log('catch:', rej);
})
all函数 / race函数并行异步操作
Promise的all函数和race函数都提供了并行异步操作的能力,二者的区别在于,当这些并行的异步操作耗时不同时,all函数是在所有的异步操作都执行完之后才会执行,而race函数则会在第一个异步操作完成之后立即执行。
function runPro1(){var a = new Promise((resolve, reject) => {setTimeout(() => {resolve('success');console.log('this is runPro1');}, 1000);});return a;
}
function runPro2(){var a = new Promise((resolve, reject) => {setTimeout(() => {resolve('success');console.log('this is runPro2');}, 2000);});return a;
};function runPro3(){var a = new Promise((resolve, reject) => {setTimeout(() => {resolve('success');console.log('this is runPro3');}, 3000);});return a;
};Promise.all([runPro1(), runPro2(), runPro3()])
.then((res) => {console.log('all:', res);
})
function runPro1(){var a = new Promise((resolve, reject) => {setTimeout(() => {resolve('success');console.log('this is runPro1');}, 1000);});return a;
}
function runPro2(){var a = new Promise((resolve, reject) => {setTimeout(() => {resolve('success');console.log('this is runPro2');}, 2000);});return a;
};function runPro3(){var a = new Promise((resolve, reject) => {setTimeout(() => {resolve('success');console.log('this is runPro3');}, 3000);});return a;
};Promise.races([runPro1(), runPro2(), runPro3()])
.then((res) => {console.log('all:', res);
})
如上两个代码片段所示,all函数和race都接收一个数组作为参数,这个数组中的值就是我们要进行并行执行的异步操作。这里我们同样使用then函数作为异步操作完成的回调函数。
同时我们通过console输出发现,在race函数的回调函数开始执行的时候,另外两个没有执行完成的异步操作并没有停止,依旧在执行。
相关文章:

JavaScript中Promise的简单使用及其原理
Promise是ES6最重要的特性之一,今天来系统且细致的研究一下Promise的用法以及原理。 按照我往常的理解,Promise是一个构造函数,有all、resolve、reject、then、catch等几个方法,一般情况下,在涉及到异步操作时才会用到…...

SpringBoot RabbitMQ 延时队列取消订单【SpringBoot系列14】
SpringCloud 大型系列课程正在制作中,欢迎大家关注与提意见。 程序员每天的CV 与 板砖,也要知其所以然,本系列课程可以帮助初学者学习 SpringBooot 项目开发 与 SpringCloud 微服务系列项目开发 1 项目准备 SpringBoot 雪花算法生成商品订单…...

【论文阅读 WWW‘23】Zero-shot Clarifying Question Generation for Conversational Search
文章目录前言MotivationContributionsMethodFacet-constrained Question GenerationMultiform Question Prompting and RankingExperimentsDatasetResultAuto-metric evaluationHuman evaluationKnowledge前言 最近对一些之前的文章进行了重读,因此整理了之前的笔记…...

ouc 网络安全实验 格式化字符串漏洞
文章目录要求lab1lab2lab3lab4结语因为当时自己做实验的时候出现了很多疑问不会解决,在网上看到了一位大佬 王森ouc 的专栏文章解决了很多问题,也学到了很多知识和解决问题的方法,现在把我的实验解决方法也发上来,希望有不会的同…...

PMSM矢量控制笔记(1.1)——电机的机械结构与运行原理
前言:重新整理以前的知识和文章发现,仍然有许多地方没有学得明白,懵懵懂懂含含糊糊的地方多如牛毛,尤其是到了真正实际写东西或者做项目时,如果不是系统的学习了知识,很容易遇到问题就卡壳,也想…...
2022年全国职业院校技能大赛(中职组)网络安全竞赛试题——中间人攻击渗透测试解析(详细)
B-4任务四:中间人攻击渗透测试 *任务说明:仅能获取Server4的IP地址 *任务说明:仅能获取Server11的IP地址 1.通过上题渗透后得到控制权限的服务器场景Server4进行查看本地的arp缓存表的操作,并将该操作所使用的命令作为Flag值提交; 2.通过上题渗透后得到控制权限的服务…...
MySQL必知必会 | 安全、维护、性能
全球化和本地化 关于MySQL处理不同字符集和语言 字符集和校对顺序 数据库被用来存储和检索数据,不同的语言和字符集需要以不同的方式存储和检索,因此,MySQL需要适应不同的字符集,适应不同的排序方式 一些术语: 字符…...
MaaS Model as a Service 模型即服务
大模型是人工智能的发展趋势和未来。大模型是“大算力强算法” 结合的产物。目前,大模型生态已初具规模。大模型能够实现 AI 从“手工作坊”到“工厂模式”的转变,大模型通常是在大规模无标注 数据上进行训练,学习出一种特征和规则…...
【编程基础】027.C语言中函数在解题中的应用(三)
文章目录C语言中函数的应用1、自定义函数实现二维数组的转置2、自定义函数之整数处理3、自定义函数之数字后移4、自定义函数之字符串拷贝C语言中函数的应用 1、自定义函数实现二维数组的转置 题目描述 写一个函数,使给定的一个二维数组(3&a…...

echart图表之highcharts
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录前言一、HighCharts是什么?二、使用步骤1.引入库2.前端代码3.展现结果4.后台自动截图总结前言 提示:这里可以添加本文要记录的大概内容&…...

关于.Net和Java的看法——我见过最牛的一个小实习生经历
1、背景 笔者(小方同学在学习)是一个专科院校的一名普通学生,目前就职于某三线城市的WEB方面.Net开发实习生,在找实习期间和就业期间的一些看法,发表此文,纯个人想法,欢迎讨论,指正…...

基于springboot+vue的“智慧食堂”程序设计实现【毕业论文,源码】
系统登录界面系统架构开发语言:Java框架:springbootJDK版本:JDK1.8服务器:tomcat7数据库:mysql 5.7数据库工具:Navicat开发软件:eclipse/myeclipse/ideaMaven包:Maven浏览器…...
学计算机选择什么编程语言好一些?
工资水平的话,目前人工智能、大数据和云计算等领域的工资相对较高,但是要求也高,学历,学习能力什么的。然后是后端开发,Python、Java、C等编程语言的工资普遍较高。 不用开发语言的优势 Java:Java是一种…...

持续集成 在 Linux 上搭建 Jenkins,自动构建接口测试
本篇把从 0 开始搭建 Jenkins 的过程分享给大家,希望对小伙伴们有所帮助。 文章目录 在 Linux 上安装 Jenkins在 Linux 上安装 Git在 Linux 上安装 Python在 Linux 上安装 Allure配置 Jenkinsjenkins 赋能 - 使用邮箱发送测试报告jenkins 赋能 - 优化测试报告内容…...
MySQL学习笔记(总结)
1. 数据库服务器操作命令 启动数据库:net start mysql80 (注释:windows命令) 停止数据库:net stop mysql80 (注释:windows命令) 重启数据库:systemctl restart mysql;…...

Android开发 Layout布局 ScrollView
1.LinearLayout 属性 orientation:内部组件排列方式,可选vertical、horizontal,默认horizontal layout_weight: 与平级组件长宽比例,需要将layout_width、layout_height其中一个设置为0dp,表明长或宽与平级组件的长…...

手撕数据结构与算法——树(三指针描述一棵树)
🏆作者主页:king&南星 🎄专栏链接:数据结构 🏅文章目录🌱树一、🌲概念与定义二、🌳定义与预备三、🌴创建结点函数四、🍀查找五、🍁插入六、&a…...
字节跳动Java后端开发实习面经
最近在和同学一起找实习,投了b站、字节和miHoYo的后端开发。b站二月底就投了,但现在也还没回复;miHoYo也还没回复,估计是只面向24届了;感谢字节,给了我面试的机会。字节真的处理好快,不到一周官…...

STM32实战项目-触摸按键
前言: 通过触摸按键控制LED灯以及继电器,具体实现功能如下: 1、触摸按键1单击与长按,控制LED1; 2、触摸按键2单击与长按,控制LED2; 3、触摸按键3单击与长按,控制LED3; 4、触摸按键4单击与长…...
安全行业-术语(万字)
肉鸡 所谓“肉鸡”说一种很形象的比喻,比喻那些可以任意被我们控制的电脑,对方可以是Windows系统,也可以说UNIX/linux系统,可以说普通的个人电脑,也可以是大型的服务器,我们可以像操作自己的电脑那样来操控…...
[特殊字符] 智能合约中的数据是如何在区块链中保持一致的?
🧠 智能合约中的数据是如何在区块链中保持一致的? 为什么所有区块链节点都能得出相同结果?合约调用这么复杂,状态真能保持一致吗?本篇带你从底层视角理解“状态一致性”的真相。 一、智能合约的数据存储在哪里…...
SciencePlots——绘制论文中的图片
文章目录 安装一、风格二、1 资源 安装 # 安装最新版 pip install githttps://github.com/garrettj403/SciencePlots.git# 安装稳定版 pip install SciencePlots一、风格 简单好用的深度学习论文绘图专用工具包–Science Plot 二、 1 资源 论文绘图神器来了:一行…...

【力扣数据库知识手册笔记】索引
索引 索引的优缺点 优点1. 通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。2. 可以加快数据的检索速度(创建索引的主要原因)。3. 可以加速表和表之间的连接,实现数据的参考完整性。4. 可以在查询过程中,…...

MongoDB学习和应用(高效的非关系型数据库)
一丶 MongoDB简介 对于社交类软件的功能,我们需要对它的功能特点进行分析: 数据量会随着用户数增大而增大读多写少价值较低非好友看不到其动态信息地理位置的查询… 针对以上特点进行分析各大存储工具: mysql:关系型数据库&am…...

(二)TensorRT-LLM | 模型导出(v0.20.0rc3)
0. 概述 上一节 对安装和使用有个基本介绍。根据这个 issue 的描述,后续 TensorRT-LLM 团队可能更专注于更新和维护 pytorch backend。但 tensorrt backend 作为先前一直开发的工作,其中包含了大量可以学习的地方。本文主要看看它导出模型的部分&#x…...

从深圳崛起的“机器之眼”:赴港乐动机器人的万亿赛道赶考路
进入2025年以来,尽管围绕人形机器人、具身智能等机器人赛道的质疑声不断,但全球市场热度依然高涨,入局者持续增加。 以国内市场为例,天眼查专业版数据显示,截至5月底,我国现存在业、存续状态的机器人相关企…...
Linux简单的操作
ls ls 查看当前目录 ll 查看详细内容 ls -a 查看所有的内容 ls --help 查看方法文档 pwd pwd 查看当前路径 cd cd 转路径 cd .. 转上一级路径 cd 名 转换路径 …...
测试markdown--肇兴
day1: 1、去程:7:04 --11:32高铁 高铁右转上售票大厅2楼,穿过候车厅下一楼,上大巴车 ¥10/人 **2、到达:**12点多到达寨子,买门票,美团/抖音:¥78人 3、中饭&a…...

MMaDA: Multimodal Large Diffusion Language Models
CODE : https://github.com/Gen-Verse/MMaDA Abstract 我们介绍了一种新型的多模态扩散基础模型MMaDA,它被设计用于在文本推理、多模态理解和文本到图像生成等不同领域实现卓越的性能。该方法的特点是三个关键创新:(i) MMaDA采用统一的扩散架构…...
使用van-uploader 的UI组件,结合vue2如何实现图片上传组件的封装
以下是基于 vant-ui(适配 Vue2 版本 )实现截图中照片上传预览、删除功能,并封装成可复用组件的完整代码,包含样式和逻辑实现,可直接在 Vue2 项目中使用: 1. 封装的图片上传组件 ImageUploader.vue <te…...