浅谈对Promise的理解以及在工作中的应用
浅谈对Promise的理解以及在工作中的应用
- Promise的概念
- 背景知识
- JavaScript的同步和异步
- JavaScript事件循环
- 回调函数进行异步操作
- 解决方案:Promise
- Promise 在工作中的运用
- 创建Promise
- Promise封装AJAX
- Promise链式操作
- Promise.all()
- Promise.race()
- async和await
- 总结
Promise的概念
在开始讲解Promise前,我们先大致了解一下js的运行机制以及多个任务是怎么运作的。
背景知识
众所周知,JavaScript是一门单线程语言,也就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就不得不一直等着,这样会造成浏览器处于假死状态,严重影响用户体验。为了解决这个问题,js引入了异步的概念,在执行任务时挂起处于等待中的任务,先运行排在后面的任务。等到刚才挂起的任务返回了结果,再回过头,把挂起的任务继续执行下去。于是,js将所有的任务分成了两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous)。
JavaScript的同步和异步
同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;
异步任务指的是,不进入主线程、而进入"任务队列"(task queue)的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行;
除了广义的同步任务和异步任务,对任务更精细的定义:
宏任务(macro-task):包括整体代码script,setTimeout,setInterval
微任务(micro-task):Promise,process.nextTick
宏任务中有微任务,一定要将宏任务中的微任务执行完毕,再去执行下一个宏任务。
那究竟什么是任务队列呢,同步任务和异步任务又是怎么执行的,这就要引入js的运行机制:事件循环(event loop)
JavaScript事件循环
js事件循环运作机制如下:
- 同步任务和异步任务分别进入不同的“场所”,同步任务进入主线程,异步任务进入Event Table (事件表)并注册函数
- 指定的事件完成后,Event Table (事件表)会将这个函数移入到Event Queue (事件队列)
- 当主线程的任务完成后,会检查Event Queue (事件队列),如果有任务就全部执行,如果没有就进入下一个宏任务
- 这个过程会不断的重复,这就叫事件循环
回调函数进行异步操作
和同步操作不同,异步操作是不会立即返回结果的(如发起网络请求,下载文件,操作数据库等)。如果我们后续的函数需要之前返回的结果,又怎样使之前的异步操作在其完成时通知到后续函数来执行呢?
通常,我们可以将这个函数先定义,存储在内存中,将其当做参数传入之前的异步操作函数中,等异步操作结束,就会调用执行这个函数,这个函数就叫做回调函数(callback)
// 下载
function download(callback){// 模拟异步操作setTimeout(function(){// 调用回调函数callback('下载完成');}, 1000);
}function callback(value){// 下载完成的处理console.log(value);
}download(callback);// 这段代码将在1秒后在控制台打印“下载完成”
但假如callback函数同样是个异步函数,且callback里又嵌入了callback呢? 例如需求是等待第一个文件下载完成后,再下载第二个文件,等待第二个文件下载完成后,再下载第三个文件…,这样的话,上面这种方法就不可取了,因为会产生很多的函数嵌套,嵌套太深容易引发回调地狱(指的是回调函数里嵌套回调函数,使得代码可读性非常差,容易陷于无止尽的循环)
//回调地狱setTimeout(function () { //第一层console.log('张三');//等3秒打印张三在执行下一个回调函数setTimeout(function () { //第二层console.log('李四');//等2秒打印李四在执行下一个回调函数setTimeout(function () { //第三层console.log('王五');//等一秒打印王五}, 1000)}, 2000)}, 3000)
解决方案:Promise
所以为了回调地狱的问题,promise方案应运而生,它是对回调方法的一种封装,是用来处理异步操作的,可以让我们写异步调用的时候写起来更加优雅,更加美观。
以下是promise的一些特点
- Promise 对象代表一个异步操作,对象的状态不受外界影响,有三种状态,pending(进行中)、fulfilled(已成功)、rejected(已失败)、settled (结束);只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态,且一旦状态改变就不可再变
- Promise对象的then方法用来接收处理成功时响应的数据,catch方法用来接收处理失败时相应的数据,其中then方法的两个参数是resolve(成功回调),reject(失败回调)。异步任务执行成功时调用resolve函数返回结果,反之调用reject,根据不同的任务,由开发者来决定resolve和reject在函数体内的位置
- then方法返回的是一个新的Promise实例(注意,不是原来那个Promise实例)。因此可以采用链式写法,即then方法后面再调用另一个then/catch方法,链式调用可以保证代码的执行顺序
promise解决刚才的地狱回调问题:
// promise解决方式
function fn(str) {var promise = new Promise(function (resolve, reject) { //resolve是成功的方法 reject是失败的方法 //处理异步任务var flag = true;setTimeout(function () {if (flag) {resolve(str)}else {reject('失败')}})})return promise;
}fn('张三').then((res) => { //then是成功执行的方法 返回的还是一个promise对象console.log(res);//打印张三 res是结果return fn('李四');}).then((res) => {console.log(res);return fn('王五')}).then((res) => {console.log(res);}).catch((res) => { //catch是失败执行的方法console.log(res);})
Promise 在工作中的运用
创建Promise
promise构造器只接收一个参数,该参数被称为执行器(executor)的函数。该函数会被传递两个参数(方法),一个叫做resolve,另一个叫做reject。
resolve函数在成功时调用,reject函数在失败时被调用。并且resolve和reject只能被使用一次,如果之后还有resolve和reject也不会被执行了,有点儿类似于return,但是不同点在于,其他代码还会被照常执行。
new Promise((resolve, reject)=> {resolve('我是第一次调用resolve');console.log('我是其他代码');resolve('我是第二次调用resolve'); // 不在起作用reject('我来调用reject'); // 不在起作用
})
也可以直接使用Promise.resolve或者Promise.reject来创建成功或者失败的Promise
let p1 = Promise.resolve('我是成功的Promise'),p2 = Promise.reject('我是失败的Promise');
Promise封装AJAX
var ajax = function(url) {return new Promise(function(resolve, reject) {var xhr = new XMLHttpRequest();xhr.open('get', url, true);xhr.send();xhr.onreadystatechange = function() {if (xhr.readystate == 4 && xhr.status == 200) {resolve(xhr.responseText)} else if (xhr.readystate == 4 && xhr.status !== 200) {reject(xhr.statusText)}}})
}
ajax.then(console.log(xhr.responseText)); //打印出返回的数据
Promise链式操作
由于Promise的then 方法始终返回一个 Promise 对象, 所以Promise 可以一直调用 then 方法,从而实现链式调用(解决地狱回调)。不管 new Promise 创建出来的执行状态是成功 / 失败,只要在 then / catch方法中通过 return 返回一个结果,不管这个值是 Promise 对象还是普通值,都可以通过链式调用的 .then 方法中获取到这个值,因为 promise.then 方法会默认在返回值的外层包裹一层 Promise 对象,这样才可以实现 Promise 一直通过 .then 的方式去链式调用
let p1 = new Promise((resolve, reject)=> {let name = '张三'setTimeout(()=> {resolve(name);}, 2000)
})let p2 = new Promise((resolve, reject)=> {let name = '李四'setTimeout(()=> {resolve(name);}, 1000)
})let p3 = new Promise((resolve, reject)=> {let name = '王五'setTimeout(()=> {resolve(name);}, 3000)
})//方式一:链式操作返回promise对象
p1.then((res) => {console.log(res+'第一个出场');return p2
}).then((res) => {console.log(res+'第二个出场');return p3
}).then((res) => {console.log(res+'第三个出场');
})//链式操作返回promise对象输出结果
张三第一个出场
李四第二个出场
王五第三个出场//方式二:链式操作返回普通值
p1.then((res) => {console.log('我是'+res);return res
}).then((res) => {console.log('我是'+res+'的儿子');return res
}).then((res) => {console.log('我是'+res+'的孙子');
})//链式操作返回普通值输出结果
张三第一个出场
李四第二个出场
王五第三个出场
Promise.all()
这个方法返回一个新的promise对象,该promise对象在参数对象promises里所有的promise对象都成功的时候才会触发成功,一旦有任何一个promises里面的promise对象失败则立即触发该promise对象的失败。这个新的promise对象在触发成功状态以后,会把一个包含promises里所有promise返回值的数组作为成功回调的返回值,顺序跟promises的顺序保持一致;如果这个新的promise对象触发了失败状态,它会把iterable里第一个触发失败的promise对象的错误信息作为它的失败错误信息。Promise.all方法常被用于处理多个promise对象的状态集合。
简而言之,all方法会将传入的异步操作并行执行,等到它们都执行完后才会进到then方法,从时间上来看取决于最后一个异步任务执行完成的时间
let p1 = new Promise((resolve, reject)=> {let name = '张三'setTimeout(()=> {resolve(name);}, 2000)
})let p2 = new Promise((resolve, reject)=> {let name = '李四'setTimeout(()=> {resolve(name);}, 1000)
})let p3 = new Promise((resolve, reject)=> {let name = '王五'setTimeout(()=> {resolve(name);}, 3000)
})// promise.all用法
Promise.all([p1,p2,p3]).then((res) => {console.log(res+'都已到达终点');
})// promise.all输出结果
张三,李四,王五都已到达终点
Promise.race()
race就是赛跑的意思,谁先出结果就由谁决定,采用第一个 promise 的值作为它的值,当promises参数里的任意一个子promise被成功或失败后,父promise马上也会用子promise的成功返回值或失败详情作为参数调用父promise绑定的相应句柄,并返回该promise对象。
let p1 = new Promise((resolve, reject)=> {let name = '张三'setTimeout(()=> {resolve(name);}, 2000)
})let p2 = new Promise((resolve, reject)=> {let name = '李四'setTimeout(()=> {resolve(name);}, 1000)
})let p3 = new Promise((resolve, reject)=> {let name = '王五'setTimeout(()=> {resolve(name);}, 3000)
})// promise.race用法
Promise.race([p1,p2,p3]).then((res) => {console.log(res+'第一个到达终点');
})// promise.all输出结果
李四第一个到达终点
async和await
async 是“异步”的简写,而 await 可以认为是 async wait(等待) 的简写。
所以应该很好理解 async 用于申明一个 function 是异步的,返回的是一个 Promise 对象.
而 await 用于等待一个异步方法执行完成,后面必须跟一个Promise对象,但是不必写then(),直接就可以得到返回值
//基本用法的async函数
let asyncFun = async function(){return 1
}
console.log(asyncFun())
//会返回一个promise对象//使用场景
//摇色子方法
function dice(){return new Promise((resolve,reject)=>{let sino = parseInt(Math.random()*6+1) //生成一个1~6之间的随机小数setTimeout(()=>{resolve(sino)},2000)})
}
//异步方法async function text(){let n= await dice()//await 关键字后面调用摇色子方法执行完毕之后,才进行变量赋值console.log("摇出来"+n) //最后打印出摇出来的数}
text()//输出结果
Promise { 1 }
摇出来5
总结
以上就是我个人关于Promise的理解以及在工作中的应用,总的来说Promise在日常开发工作中的使用还是比较多的,他最大的用途在于让多个异步的任务按照我们想要的方式去执行。但想要彻底理解promise的运行方式及原理,还需要了解js引擎的运行逻辑,任务队列、宏任务微任务等。上文中如果有错误的地方欢迎指正,大家共同进步,越秃越强!
相关文章:
浅谈对Promise的理解以及在工作中的应用
浅谈对Promise的理解以及在工作中的应用Promise的概念背景知识JavaScript的同步和异步JavaScript事件循环回调函数进行异步操作解决方案:PromisePromise 在工作中的运用创建PromisePromise封装AJAXPromise链式操作Promise.all()Promise.race()async和await总结Promi…...
开源|快速入门和理解并模拟实现GPS户外机器人的定位与导航
户外机器人的定位导航相对于需要建图的场景来说,是比较简单容易实现的,因为可以借助第三方地图完成定位,并在第三方地图中完成路径规划和下发航点等操作,实现的难题在于如何控制机器人完成步行和转弯。 这些在不引进RTK高精度定位…...
Java多线程系列--synchronized的原理
原文网址:Java多线程系列--synchronized的原理_IT利刃出鞘的博客-CSDN博客 简介 本文介绍Java的synchronized的原理。 反编译出字节码 Test.java public class Test {private static Object LOCK new Object();public static int main(String[] args) {synchro…...
QEMU启动ARM64 Linux内核
目录前言前置知识virt开发板ARM处理器家族简介安装qemu-system-aarch64安装交叉编译工具交叉编译ARM64 Linux内核交叉编译ARM64 Busybox使用busybox制作initramfs使用QEMU启动ARM64 Linux内核前言 本文介绍采用 qemu 模拟ARM-64bit开发板(针对ARM-32bit的有另一篇文…...
Linux->进程程序替换
目录 前言: 1 程序替换原理 2 单进程替换 3 替换函数 3.1 函数使用 4 程序去替换自己的另一个程序操作方式 5 实现自己的shell 前言: 通过我们之前对于子进程的应用,我相信大家一定是能够想到创建子进程的目的之一就是为了代劳父进程执…...
最强分布式锁工具:Redisson
1 Redisson概述1.1 什么是Redisson?Redisson是一个在Redis的基础上实现的Java驻内存数据网格(In-Memory Data Grid)。它不仅提供了一系列的分布式的Java常用对象,还提供了许多分布式服务。其中包括(BitSet, Set, Multimap, Sorted…...
Java9-17新特性
Java9-17新特性 一、接口的私有方法 Java8版本接口增加了两类成员: 公共的默认方法公共的静态方法 Java9版本接口又新增了一类成员: 私有的方法 为什么JDK1.9要允许接口定义私有方法呢?因为我们说接口是规范,规范时需要公开…...
电脑开机找不到启动设备怎么办?
电脑正常开机,却提示“找不到启动设备”,这时我们该怎么办呢?本文就为大家介绍几种针对该问题的解决方法,一起来看看吧!“找不到启动设备”是什么意思?可引导设备(又称启动设备)是一…...
使用langchain打造自己的大型语言模型(LLMs)
我们知道Openai的聊天机器人可以回答用户提出的绝大多数问题,它几乎无所不知,无所不能,但是由于有机器人所学习到的是截止到2021年9月以前的知识,所以当用户询问机器人关于2021年9月以后发送的事情时,它无法给出正确的答案&#x…...
assert()宏函数
assert()宏函数 assert是宏,而不是函数。在C的assert.h文件中 #include <assert.h> void assert( int expression );assert的作用是先计算表达式expression, 如果其值为假(即为0),那么它会打印出来assert的内容…...
Docker圣经:大白话说Docker底层原理,6W字实现Docker自由
说在前面: 现在拿到offer超级难,甚至连面试电话,一个都搞不到。 尼恩的技术社群(50)中,很多小伙伴凭借 “左手云原生右手大数据”的绝活,拿到了offer,并且是非常优质的offer&#…...
Redis+Caffeine多级(二级)缓存,让访问速度纵享丝滑
目录多级缓存的引入多级缓存的优势CaffeineRedis实现多级缓存V1.0版本V2.0版本V3.0版本多级缓存的引入 在高性能的服务架构设计中,缓存是一个不可或缺的环节。在实际的项目中,我们通常会将一些热点数据存储到Redis或MemCache这类缓存中间件中࿰…...
C#和.net框架之第一弹
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录C# 简介一、微软平台的编程二、使用VS创建第一个c#程序1、第一步2、第二步3、第三步4、第四步5、第五步C# 简介 C# 是一个现代的、通用的、面向对象的编程语言&…...
C++---背包模型---潜水员(每日一道算法2023.3.12)
注意事项: 本题是"动态规划—01背包"和"背包模型—二维费用的背包问题"的扩展题,优化思路不多赘述,dp思路会稍有不同,下面详细讲解。 题目: 潜水员为了潜水要使用特殊的装备。 他有一个带2种气体…...
C++类的成员变量和成员函数详解
类可以看做是一种数据类型,它类似于普通的数据类型,但是又有别于普通的数据类型。类这种数据类型是一个包含成员变量和成员函数的集合。 类的成员变量和普通变量一样,也有数据类型和名称,占用固定长度的内存。但是,在定义类的时候不能对成员变量赋值,因为类只是一种数据类…...
(枚举)(模拟)(位运算)116. 飞行员兄弟
目录 题目链接 一些话 切入点 流程 套路 ac代码 题目链接 116. 飞行员兄弟 - AcWing题库 我草,又~在~水~字~数~啦!我草,又~在~水~字~数~啦…...
详解Array.prototype.shift.call(arguments)
经常看到如下代码: function foo() {let k Array.prototype.shift.call(arguments);console.log(k) } foo(11,22) //11 Array.prototype.shift.call(arguments)的作用是: 取 arguments 中的第一个参数 一、为啥要这么写,不直接使用argume…...
Tina_Linux_Wi-Fi_开发指南
Tina Linux Wi-Fi 开发指南 1 前言 1.1 文档简介 介绍Allwinner 平台上Wi-Fi 驱动移植,介绍Tina Wi-Fi 管理框架,包括Station,Ap 以及Wi-Fi 常见问题。 1.2 目标读者 适用Tina 平台的广大客户和对Tina Wi-Fi 感兴趣的同事。 1.3 适用范…...
Spring AOP(AOP概念、组成、Spring AOP实现及实现原理)
文章目录1. Spring AOP 是什么2. 为什么要用 AOP3. 怎么学 Spring AOP4. AOP 组成5. Spring AOP 实现5.1 添加 Spring AOP 框架支持5.2 定义切面和切点5.3 实现通知方法5.4 使⽤ AOP 统计 UserController 每个⽅法的执⾏时间 StopWatch5.4 切点表达式说明 AspectJ6. Spring AOP…...
8.条件渲染指令
目录 1 v-if v-show 2 v-if v-else-if v-else 1 v-if v-show v-if与v-show都可以控制DOM的显示与隐藏 由于flag是布尔值,所以这里可以直接写 v-if"flag" 当flag为true的时候,v-if与v-show控制的div都会被显示出来 当flag为false的时候&a…...
2023年全网最全最细最流行的自动化测试工具有哪些?你都知道吗!
下面就是我个人整理的一些比较常用的自动化测试工具,并且还有视频版本的详细介绍,同时在线学习人数超过1000人! B站讲的最详细的Python接口自动化测试实战教程全集(实战最新版)一:前言 随着测试工程师技能和…...
网络安全——数据链路层安全协议
作者简介:一名云计算网络运维人员、每天分享网络与运维的技术与干货。 座右铭:低头赶路,敬事如仪 个人主页:网络豆的主页 目录 前言 一.数据链路层安全协议简介 1.数据链路安全性 二.局域网数据链路层协议 1.…...
编译原理基础概念
一、什么是编译程序编译程序是一种程序,能够将某一种高级语言编写的源程序改造成另一种低级语言编写的目标程序,他们在逻辑上等价、完成相同的工作二、编译阶段1、当目标程序是机器语言时,编译阶段:(1)编译…...
蔬菜视觉分拣机器人的设计与实现(RoboWork参赛方案)
蔬菜视觉分拣机器人的设计与实现 文章目录蔬菜视觉分拣机器人的设计与实现1. 技术栈背景2. 整体设计3. 机械结构3.1 整体结构3.2 底座结构3.3 小臂结构3.4 大臂结构3.5 负载组件结构3.6 末端执行器结构4. 硬件部分4.1 视觉系统4.1.1 光源4.1.2 海康工业相机4.2 传送带系统4.2.1…...
【LVGL移植】STM32F1基于STM32CubeMX配置硬件SPI驱动1.8寸TFT ST7735S跑LVGL图形demo
【LVGL移植】STM32F1基于STM32CubeMX配置硬件SPI驱动1.8寸TFT ST7735S屏幕跑LVGL图形demo🎬运行LVGL 按键组件demo ✨基于STM32CubeMX配置工程是因为方便移植,只要是STM32芯片,拿到我的这个工程源码就可以根据自己的stm32芯片,自…...
写给20、21级学生的话
写给20、21级学生的话前言一、关于招聘变招生,你怎么看?二、对于即将实习/已经实习的学生,你有什么建议?1.学习方面2.提升方面三、思想成年真的很重要前言 最近,有一些同学遇到的实习问题,我统一回复下&…...
功能测试用例多次录制后,我丢掉了selenium,选择龙测AI-TestOps云平台
目录一、如何使用龙测AI-TestOps云平台1、进入龙测AI-TestOps云平台2、新建项目3、新建流程图4、创建任务5、查看任务状态6、查看报告、图片7、下载流程图、测试报告、excel用例二、龙测AI-TestOps云平台AI功能介绍1、NLP2、视频AI转流程图三、总结功能测试用例多次录制后&…...
【C++知识点】C++20 常用新特性总结
✍个人博客:https://blog.csdn.net/Newin2020?spm1011.2415.3001.5343 📚专栏地址:C/C知识点 📣专栏定位:整理一下 C 相关的知识点,供大家学习参考~ ❤️如果有收获的话,欢迎点赞👍…...
数据库体系结构概念--集中式数据库、分布式数据库
数据库模式 前言: 平时我们接触的‘数据库’一般指的是DBMS,数据库管理系统,DBMS是软件如:mysql、oracle、dm等等都是集中式数据库,但它们不能代表整个数据库,只是通过这些软件来管理相应的数据内容&#…...
PyQt5数据库开发2 5.2 QSqlRelationalTableModel
目录 一、Qt窗体设计 1. 新建Qt项目 2. 添加组件 3. 添加资源 4. 添加Action 5. 添加工具栏 6. 添加菜单项 7. 添加退出功能 二、SQL Server下建表插数据 1. 建立表 2. 插入数据 3. 单表数据 4. 联合查询 三、代码实现 1. 新建项目目录 2. 编译窗体文件和资…...
中国建设银行官网电脑版/网站关键词排名优化方法
建议:当前安装方式仅限于开发、测试等研究使用. Docker 常用命令 Docker 安装系列 Docker mongo:5.0 安装 Docker portainer(Docker管理工具) Docker 安装Reids Docker 安装 Redis的完全体版本RedisMod Docker安装 a…...
网站首页被k 不恢复/seo的基本步骤
Ubuntu14.04下安装VMware安装过程记录: 目前博主了解的Linux下有VirtualBox和VMware两大虚拟机。 VirtualBoxh 优点:免费,在Ubuntu的软件中心找到或者用 sudo apt-getinstall virtualbox 命令安装。 缺点:文件不能拖拽、U盘不能…...
三六五网做网站吗/免费网站模板
作为局域网的主要连接设备,以太网交换机成为应用普及最快的网络设备之一,同时,也是随着这种快速的发展,交换机的功能不断增强,随之而来则是交换机端口的更新换代以及各种特殊设备连接端口不断的添加到交换机上…...
高端网站开发课程sublime/西地那非片能延时多久
如果之间有防火墙的话,还要注意:要使Oracle客户端能正常连接到设置有防火墙的Oracle服务器,单开放一个1521或自定义的监听端口是不够的。昨天晚上为了测试BOM的多层转单层程序,而需要连接到服务器上的Oracle将数据导入。因为服务器…...
郑州品牌网站建设/竞价系统
有媒体报道:截至2010年12月底,中国移动3G用户总数2070.2万户。而在新增用户方面,中移动2010年12月份新增3G用户186.7万户,较2010年11月有大幅下降。2010年11月,中国移动新增3G用户298万。看到这则消息我笑了࿰…...
做淘宝网站多少钱/重庆网络推广专员
时间进入到3月份,春天的气息也好似弥漫到了整个手机圈,一年中新机的高产期近在眼前,近期有换机需求的同学可要擦亮双眼了。机情问答:6000元买三星or苹果?努比亚α能玩吃鸡吗本周一,独立成为子品牌的红米&am…...