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系统,可以说普通的个人电脑,也可以是大型的服务器,我们可以像操作自己的电脑那样来操控…...

多模态2025:技术路线“神仙打架”,视频生成冲上云霄
文|魏琳华 编|王一粟 一场大会,聚集了中国多模态大模型的“半壁江山”。 智源大会2025为期两天的论坛中,汇集了学界、创业公司和大厂等三方的热门选手,关于多模态的集中讨论达到了前所未有的热度。其中,…...
论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(二)
HoST框架核心实现方法详解 - 论文深度解读(第二部分) 《Learning Humanoid Standing-up Control across Diverse Postures》 系列文章: 论文深度解读 + 算法与代码分析(二) 作者机构: 上海AI Lab, 上海交通大学, 香港大学, 浙江大学, 香港中文大学 论文主题: 人形机器人…...

工业安全零事故的智能守护者:一体化AI智能安防平台
前言: 通过AI视觉技术,为船厂提供全面的安全监控解决方案,涵盖交通违规检测、起重机轨道安全、非法入侵检测、盗窃防范、安全规范执行监控等多个方面,能够实现对应负责人反馈机制,并最终实现数据的统计报表。提升船厂…...

《Qt C++ 与 OpenCV:解锁视频播放程序设计的奥秘》
引言:探索视频播放程序设计之旅 在当今数字化时代,多媒体应用已渗透到我们生活的方方面面,从日常的视频娱乐到专业的视频监控、视频会议系统,视频播放程序作为多媒体应用的核心组成部分,扮演着至关重要的角色。无论是在个人电脑、移动设备还是智能电视等平台上,用户都期望…...
深入浅出:JavaScript 中的 `window.crypto.getRandomValues()` 方法
深入浅出:JavaScript 中的 window.crypto.getRandomValues() 方法 在现代 Web 开发中,随机数的生成看似简单,却隐藏着许多玄机。无论是生成密码、加密密钥,还是创建安全令牌,随机数的质量直接关系到系统的安全性。Jav…...

无法与IP建立连接,未能下载VSCode服务器
如题,在远程连接服务器的时候突然遇到了这个提示。 查阅了一圈,发现是VSCode版本自动更新惹的祸!!! 在VSCode的帮助->关于这里发现前几天VSCode自动更新了,我的版本号变成了1.100.3 才导致了远程连接出…...

SpringBoot+uniapp 的 Champion 俱乐部微信小程序设计与实现,论文初版实现
摘要 本论文旨在设计并实现基于 SpringBoot 和 uniapp 的 Champion 俱乐部微信小程序,以满足俱乐部线上活动推广、会员管理、社交互动等需求。通过 SpringBoot 搭建后端服务,提供稳定高效的数据处理与业务逻辑支持;利用 uniapp 实现跨平台前…...

【单片机期末】单片机系统设计
主要内容:系统状态机,系统时基,系统需求分析,系统构建,系统状态流图 一、题目要求 二、绘制系统状态流图 题目:根据上述描述绘制系统状态流图,注明状态转移条件及方向。 三、利用定时器产生时…...
HTML前端开发:JavaScript 常用事件详解
作为前端开发的核心,JavaScript 事件是用户与网页交互的基础。以下是常见事件的详细说明和用法示例: 1. onclick - 点击事件 当元素被单击时触发(左键点击) button.onclick function() {alert("按钮被点击了!&…...
LeetCode - 199. 二叉树的右视图
题目 199. 二叉树的右视图 - 力扣(LeetCode) 思路 右视图是指从树的右侧看,对于每一层,只能看到该层最右边的节点。实现思路是: 使用深度优先搜索(DFS)按照"根-右-左"的顺序遍历树记录每个节点的深度对于…...