Promise这样理解更简单

news2024/10/7 4:33:55

一、Promise小白怎么用?从一个故事开始吧

1、先来一段废话故事

您是一名在古老迷失城市中探险的冒险家。您身处一间装饰华丽的房间中,四周布满了古老的壁画和雕塑。您发现有两个通道分别通向不同的方向,分别是:一个黑暗的通道和一个明亮的通道。

黑暗的通道中充满了神秘的气息,您能感受到其中蕴含的古老力量。您并不知道它会带您去哪里,但您可以感受到其中蕴藏的危险和未知。另一方面,明亮的通道看起来温暖舒适,您可以看到远处照耀的阳光。您能听到一些欢快的音乐声,似乎在那个方向上有一些人在欢庆。

现在,您需要在这两个通道之间做出选择。您会选择哪一个通道呢?您的选择将会影响您接下来的冒险之旅。

2、再来看一段神秘的小短片

https://drive.weixin.qq.com/s?k=AOoAUwfiAAwVgJbsSaAUEA_QbjAFk

3、这么久了还不进入正题

先来了解几个单词,几个Promise的术语

● Promise的三个状态:pending(等待状态)、fulfilled(已成功状态)和 rejected(已失败状态)

● Promise的三个常用回调函数(咱说的是常用):

1. 实例化对象时传入的回调函数

2. then方法的传入的两个回调函数

3. catch方法传入的一个回调函数

以作业为例,继续深入了解Promise

1、开始写作业,此时Promise状态为pending,我们可以理解为初始状态,也可以理解为业务处理中

2、作业完成情况有两种,一种是写完了,一种是被狗吃了

3、无论哪种情况,必须要告诉老师,成功了就通过resolve通道暂存数据,同时会更改状态为成功fulfilled;失败了就通过reject通道暂存数据,同时修改状态为rejected。

4、老师如何获取resolve或reject通道传来的数据呢?可以使用Promise对象的.then方法获取resolve或reject的结果,或者使用.catch方法获取reject通道的信息。

5、简单来一段代码看看

const p = new Promise(function (resolve, reject) {
  resolve('通过成功通道存储数据')
})

//...如果不使用then,提取,则数据一直暂存在p对象中

//提取结果
p.then(function (val) {
  console.log(val)
})

6、这些写法都是固定的,建议使用promise之前一定要充分了解回调函数

7、咱们再升级一下,写作业用了5秒钟

const p1 = new Promise(function (resolve, reject) {
  //写作业用了5秒钟,5秒钟之后,把成功的作业暂存到resolve通道中
  setTimeout(() => {
    resolve('这是作业')
  }, 5000)
})
//上面代码中调用了resolve的时候,then里面的回调函数才会触发,这里有个时间差或者时空差的感觉
p1.then(function (val) {
  console.log(val)
})

8、再升级一下,这作业他也有被狗吃了的时候,我们假定5秒钟之外就被狗吃了,5秒钟之内就是完成的

const p2 = new Promise(function (resolve, reject) {
  //生成一个1~6秒之间的随机数
  const time = Math.random() * 4000 + 2000
  setTimeout(() => {
    if (time <= 5000) {
      resolve('成功交作业啦')
    } else {
      reject(`作业被狗吃了,耗费了${time秒}`)
    }
  }, time)
})
//成功使用then来接,失败了怎么拿到参数呢?
//用then的第二个参数来拿失败的参数
p2.then(function (val) {
  console.log(val)
}, function (err) {
  console.log('估计是被狗吃了', err)
})

9、除了then的第二个参数可以拿到失败的结果,还可以通过catch方法拿到结果,一会再讨论这两种用法的区别,先看catch的用法,注意这里需要连用

p2.then(function (val) {
  console.log(val)
}).catch((reason) => {
  //输出失败原因,大概率是被狗吃了
  console.log('估计是被狗吃了', reason)
})

10、再看一种常用的连用的写法

new Promise(function (resolve, reject) {
  //生成一个1~6秒之间的随机数
  const time = Math.random() * 4000 + 2000
  setTimeout(() => {
    if (time <= 5000) {
      resolve('成功交作业啦')
    } else {
      reject(`作业被狗吃了,耗费了${time}秒`)
    }
  }, time)
}).then(function (val) {
  console.log(val)
}).catch((reason) => {
  //输出失败原因,大概率是被狗吃了
  console.log('估计是被狗吃了', reason)
})

11、一些需注意的地方

        1.  resolve和reject只是一个形参的名字,对应实际的值是promise内部的函数,调用这两个其实调用的就是promise内部的某个函数而已,这个名字可以随便去改,例如

new Promise(function (ok, fail) {
  //此时此刻形参叫ok,但实际代表的是promise内部函数
  ok('ojbk')
}).then((res) => {
  //promise内部会存储数据,传给res这个变量,此时res 值就是ojbk
  console.log(res)
})

        2.  new Promise(构造器的参数是一个函数),这个函数会同步执行,代码执行到这里的时候就会立即执行

12、小结

1.  Promise通过构造函数同步执行,执行结果调用promise的内部函数存储,通常叫resolve和reject,一个是成功时存储存储使用的通道,另一个是失败时存储的通道,无论存储到哪个通道中,都是写代码的人去定义的

2.  then有两个参数,分别是两个函数类型的参数,第一个参数是在调用resolve时会触发,第二个参数是在调用reject时触发

3.  catch方法可以替代then的第二个参数,拿到reject结果

13、附赠then第二个参数和catch的区别

在then的第一个参数里面的代码,如果出现异常的时候,不用手动的try...catch,通过promise实例对象的.catch可以捕获then内出现的异常,但注意,catch不会捕获构造函数代码中的错误,来看例子

new Promise(function (ok, fail) {
  setTimeout(() => {
    //故意5秒后触发k的报错
    console.log(k)
  }, 5000)
}).then((res) => {
  console.log(res)
}).catch(error => {
  //这个时候,error是拿不到那个错误的,他不负责console.log(k)所在代码块中出现的错误
  console.log(error)
})

再看一个catch方法能捕获什么地方的错误

大概就是这么个大概

二、为什么要这么用,图个啥~

使用 Promise 的主要原因是他可以解决回调地狱(回调嵌套)问题,让代码变得更优雅,逻辑更清晰

举一个生活中的例子,早上起床第一件事是要穿拖鞋,第二件事是洗漱,第三件事是穿衣服,第四件事是查看“身手要钱”,第五件事是打开自家房门出去开车上班,每件事都需要串行,每一步与下一步都有关联关系

function foo() {
  //1、穿拖鞋开始
  setTimeout(() => {
    //1、2秒后穿拖鞋完成
    //2、洗漱开始
    setTimeout(() => {
      //2、2秒后洗漱完成
      //3、穿衣服开始
      setTimeout(()=>{
        //3、穿衣服完成
        //....不好意思看官,后边还有好几个步骤咱就意思一下,再写就吐了
      },2000)
    }, 2000)
  }, 2000)
}
foo()

就写这几层吧,是不是太恶心了,下面我们使用Promise来改造一下

new Promise((resolve, reject) => {
  //1、穿拖鞋
  setTimeout(() => {
    resolve('穿拖鞋搞定')
  }, 2000)
}).then(val => {
  //等待穿拖鞋完成后,会调用这个函数
  //2、洗漱
  //注意此处!!!,必须使用return 返回一个新的promise来完成链式调用
  const p = new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('洗漱搞定')
    }, 2000)
  })
  return p
}).then(val => {
  //3、穿衣服,此处直接返回,没有使用中间变量
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('穿衣服搞定')
    }, 2000)
  })
}).then(val => {
  //4、查看“身手要钱”
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('查看“身手要钱”搞定')
    }, 2000)
  })
}).then(val => {
  //5、开车去上班
  // 元气满满的一天
  console.log(val+'元气满满的一天')
})

就图这~

三、Promise其他方法

那么多方法,不讲那么多,race、all什么的网上一抓一大把

说说语法糖await和async的用法

先了解一个基础规则

● await必须修饰的是Promise对象

● await必须在async中使用

● await只能接收resolve通道的结果,reject结果会导致报错

● await修饰的代码在执行时,代码是卡住的,类似于alert,这句代码不执行完,后边的代码不会向下执行,这也类似于线程同步执行的概念,这也是await有用之处

● async修饰的必须是函数

● async修饰后的函数在执行之后会转为Promise对象

看一段简单的代码

async function foo() {
  let k = await new Promise(function (resolve, reject) {
    setTimeout(() => {
      resolve('qfedu')
    }, 2000)
  })
  console.log(k)
}

foo()

这样用倒是更麻烦,我们把要处理的事黏黏糊糊多弄一些试一试

async function foo() {
  let level1 = await new Promise((resolve, reject) => {
    //1、穿拖鞋
    setTimeout(() => {
      resolve('穿拖鞋搞定')
    }, 1000)
  })
  //拿着第一步的结果,去第二步进行操作

  let level2 = await new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(level1 + '洗漱搞定')
    }, 1000)
  })

  let level3 = await new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(level2 + '穿衣服搞定')
    }, 1000)
  })

  let level4 = await new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(level3 + '查看“身手要钱”搞定')
    }, 1000)
  })

  console.log(level4 + ',之后开车去上班,元气满满的一天')
}

foo()

输出结果:

这样代码看起来更加简洁,当然要重点考虑的问题是在整个从上到下的调用过程中,任何一个环节出现问题,都会影响下面的代码

再来,我们把代码聚焦到foo()方法调用之前和调用之后

console.log(1)
foo()  //这个会输出 穿拖鞋搞定洗漱搞定穿衣服搞定查看“身手要钱”搞定......等
console.log(2)

思考一下,程序输出的顺序

注意,使用async包裹的代码,属于异步代码,会在同步代码之后执行

我们给按钮添加一个点击事件,看点击按钮如何让程序使用await顺序执行

async function foo() {
  let level1 = await new Promise((resolve, reject) => {
    //1、穿拖鞋
    setTimeout(() => {
      resolve('穿拖鞋搞定')
    }, 1000)
  })
  //拿着第一步的结果,去第二步进行操作

  let level2 = await new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(level1 + '洗漱搞定')
    }, 1000)
  })

  let level3 = await new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(level2 + '穿衣服搞定')
    }, 1000)
  })

  let level4 = await new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(level3 + '查看“身手要钱”搞定')
    }, 1000)
  })

  console.log(level4 + ',之后开车去qfedu上班,元气满满的一天')
}

window.onclick = foo;
//或者是
window.onclick = async function(){
  //todo ...
  //await new Promise...
  //await new Promise...
  //await new Promise...
  //...
}

实际场景中,await 与async通常用来处理ajax请求类代码,让代码更简洁,再次强调,await只接收resolve结果,注意reject和error的错误要使用try...catch...处理异常

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

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

相关文章

Hive源码阅读环境准备

源码地址 hive源码地址在github或gitee均可以下载&#xff0c;如果没有vpn工具&#xff0c;使用gitee网速更快点。 github地址为: https://github.com:edingbrugh/hive.gitgitee地址如下: https://gitee.com/apache/hive.git环境要求 本地或远程已经安装hivejdk 8maven 3.6…

高权限注入跨库注入

简介 上篇文章中我们讲述了如何进行手工注入&#xff0c;也通过墨者学院的靶场&#xff0c;真实的感受了SQL注入的感觉。这篇文章我们将继续介绍SQL注入。 高权限注入 含义 世界上有千千万万的网站&#xff0c;我们也都知道网站需要部署在服务器上&#xff0c;但是如果一台…

C++基础知识-----命名空间

本期开始我们来对C进行学习 目录 1.C关键字 2.命名空间 3.C的输入与输出 1.C关键字 C总计63个关键字&#xff0c;C语言32个关键字 asmdoifreturntrycontinueautodoubleinlineshorttypedefforbooldynamic_castintsignedtypeidpublicbreakelselongsizeoftypenamethrowcase…

摘得重磅奖项,发表精彩演讲,深度参编报告!美创闪耀CSA GCR大会

4月13日&#xff0c;第六届云安全联盟大中华区大会在上海举办&#xff0c;大会由联合国数字安全联盟、上海市经济和信息化委员会、上海市委网络安全和信息化委员会办公室、上海市普陀区人民政府指导&#xff0c;云安全联盟大中华区主办。 作为零信任数据安全践行者&#xff0c;…

java银行ATM机模拟系统dzkf94

目录 摘要 I Abstract II 第1章 绪论 1 1.1 ATM简介 1 1.2 ATM发展 1 1.3 开发意义 1 1.4 论文的组成 1 1.5 本章小结 2 第2章 系统分析 3 2.1 功能需求分析 3 2.2 业务流程分析 4 2.3 数据流程分析 5 2.4 本章小结 7 第3章 系统开发技…

车载网络 - Autosar网络管理 - 常用缩写

为了方便大家日常工作中的使用和交流&#xff0c;每块专业规范或者文章中&#xff0c;都会有或多或少的缩写使用&#xff0c;然而如果一段时间没使用&#xff0c;经常会忘记这些缩写到底代表的是什么意思&#xff0c;为了方便后续内容的介绍&#xff0c;也为了我自己后面忘记后…

【数据分析之道-NumPy(四)】numpy广播机制

文章目录 专栏导读1、广播机制2、一维数组和二维数组的广播3、二维数组和三维数组的广播4、标量和数组的广播5、形状不兼容的数组不能进行广播专栏导读 ✍ 作者简介:i阿极,CSDN Python领域新星创作者,专注于分享python领域知识。 ✍ 本文录入于《数据分析之道》,本专栏针对…

MySQL---数据类型

文章目录前言一、数据类型分类二、数值类型1.tinyint类型2.bit类型三、小数类型1.float2. decimal三、字符串类型1.char2.varchar3.char和varchar比较四、日期和时间类型五、enum和set我们如何找到性别是男或者女呢?我们如何找到爱好有rapper呢?总结前言 正文开始!!! 一、数…

MyBatis 批量插入的正确姿势

近日,项目中有一个耗时较长的Job存在CPU占用过高的问题,经排查发现,主要时间消耗在往MyBatis中批量插入数据。mapper configuration是用foreach循环做的,差不多是这样。 这个方法提升批量插入速度的原理是,将传统的: INSERT INTO `table1` (`field1`, `field2`) VALUES …

Linux服务器怎么关闭防火墙?

Linux服务器怎么关闭防火墙&#xff1f; 在很多情况下&#xff0c;防火墙都会组织一些端口号的通讯。 比如我们的tomcat&#xff0c;nginx&#xff0c;redis明明安装的没问题&#xff0c;但在外部就是访问不了&#xff0c;那很有可能就是防护墙的原因了。我是艾西&#xff0c;…

protobuf编码格式解析

示例 假如定义一个如下的protobuf类型 message Person {required string user_name 1;optional int64 favorite_number 2;repeated string interests 3; }将其赋值为: user_name : "Martin" favorite_number : 1337 interests:"daydrea…

pyLoad远程代码执行漏洞复现(CVE-2023-0297)

1、产品简介 pyLoad是一个用 Python 编写的免费和开源下载管理器&#xff0c;可用于NAS、下一代路由器、无头家庭服务器以及任何能够连接到互联网并支持 Python 编程语言的设备。 2、漏洞概述 pyLoad 存在代码注入漏洞&#xff0c;未经身份验证的攻击者可以通过滥用 js2py 功能…

c/c++:类型限定符,printf输出格式,putchar,scanf,getchar

c/c&#xff1a;类型限定符&#xff0c;printf输出格式&#xff0c;putchar&#xff0c;scanf&#xff0c;getchar 2022找工作是学历、能力和运气的超强结合体&#xff0c;遇到寒冬&#xff0c;大厂不招人&#xff0c;此时学会c的话&#xff0c; 我所知道的周边的会c的同学&am…

结构方程模型(SEM)高阶应用系列

结构方程模型&#xff08;Structural Equation Modeling&#xff09;是分析多变量间因果关系的利器&#xff0c;在众多学科领域具有巨大应用潜力。我们前期推出的《基于R语言结构方程模型》通过结构方程原理介绍、结构方程全局和局域估计、模型构建和调整、潜变量分析、复合变量…

docker-compose安装prometheus告警系统

docker-compose安装Prometheus一、概述一、docker-compose二、配置文件一、概述 本文只有监控与告警的安装、告警发送、发送模版的配置。没有数据展示监控数据UI工具 一、docker-compose 1&#xff09;docker-compose.yaml version: 3.0 services:#1.prometheusprometheus:…

springcloud学习总结

springcloud 构建微服务项目步骤 导入依赖编写配置文件开启这个功能 Enablexxx配置类 于2023年2月24日下午17点38分开始学习于2023年3月17日晚上20点26分学完总结代码地址&#xff1a;https://gitee.com/liang-weihao/StudySpringcloud学习笔记地址&#xff1a;https://www.…

【Linux-MYSQL】数据库的使用

目录 1.数据库介绍 &#x1f308;1.1数据库的分类 &#x1f308;1.2存储引擎 2.数据库的操作 &#x1f308;2.1创建数据库 ​编辑&#x1f47f; 1.查show databases; &#x1f47f;2删除 drop database库名 &#x1f47f;3.创建 create 查看创建数据库的详细信息 &…

朝花夕拾 - 卷王的自白(光头祭天,法力无边》

一、震撼开场 做一个卷王 ta 有什么错&#xff0c;无非就是 ——「秃」了那么一点点&#xff01;&#xff01;&#xff01;咳咳咳&#xff0c;一一回复&#xff1a;自愿的没有想不开没有考到寺庙心态正常……如果非要给这次的行为贯穿一个理由&#xff0c;那就是「下周四就 28 …

Nfinity.io ——Web3.0创新性内容创作和社交一站式平台,引领NFT社交新时代

Nfinity.io 是什么&#xff1f; Nfinity.io 是一个Web3.0时代创新性内容创作和社交一站式平台&#xff0c;旨在彻底改变创作者、平台和粉丝的互动关系。该平台允许用户在同一个平台访问他们在 TikTok、YouTube 和 Twitter 等热门社交平台上喜欢/关注的创作者。此外&#xff0c…

图形系统:简简单单学习WindowManagerService的启动流程

作者&#xff1a;新小梦 在系统启动流程中&#xff0c;Zygote进程通过fork自己来创建SystemServer进程。SystemServer进程的主要职责之一就是创建SystemServiceManger&#xff0c;使自己成为服务的注册中心&#xff0c;并启动三种不同类型的服务&#xff1a;引导服务、核心服务…