JS中的异步与Promise使用

news2024/11/18 1:49:46

同步与异步

我们知道JS是一个单线程的语言,即在同一时间只能做一件事情。为什么设计为当线程呢。?在早期JS是为了在浏览器中运行,我们可以利用JS来制作一些页面的效果也可以和用户做一些交互。所以设计为单线程也是为了避免复杂度。比如在网页上有若干个操作,也就是在主线程中有多个任务,一个线程任务是在某个DOM节点上添加内容,另一个线程任务是删除这个节点,这时浏览器应该以哪个线程为准呢。?这就复杂了

基本概念

上面说过由于JS被设计成为一种单线程语言,即同一时间只能做一件事,这件事做完了才会做后面的事,如果当前处理的任务比较长,那后面的任务就一直没有机会被执行了。所以就出现了异步的机制。相对异步,也就有了同步的概念。其实同步和异步是一种消息通知机制在JS中有几个概念区分一下:

  • 同步任务:指的是在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;

  • 异步任务:指的是不进入主线程、而进入"任务队列"(task queue)的任务,只有等主线程任务执行完毕,"任务队列"开始通知主线程,请求执行任务,该任务才会进入主线程执行

  • 阻塞:简单来说就是,A调用B,A在等待B返回结果之前,如果当前的线程是处于挂起状态,那么就叫阻塞。

  • 非阻塞:与阻塞相反,如果A调用B,当前线程仍处于运行状态,就意味当前线程是可以的继续处理其他任务,但要时不时的去看下是否有结果了,这就是非阻塞。而等B执行完又结果之后可以通过回调的方式再处理

所以这里需要注意判断阻塞还是非阻塞和是不是同步/异步没有关系,主要看当前的线程处于挂起状态。并不是说同步一定是阻塞,而异步一定是非阻塞的。在其他有的编程语言中也有同步非阻塞和异步阻塞的。而在浏览器和Nodejs中是通过事件循环机制来实现异步非阻塞的。

下图中表示了在浏览器中的事件循环机制,主线程代码运行的时候,产生堆(heap)和栈(stack),栈中的代码调用各种外部API,它们在"任务队列"中加入各种事件(click,load,done)。只要栈中的代码执行完毕,主线程就会去读取"任务队列",依次执行那些事件所对应的回调函数。
在这里插入图片描述

异步处理方案

我们知道在早期ES6之前,使用的是回调的方式解决异步,缺点是很容易造成回调地狱。而在ES6之后就有了Promise。下面就重点介绍一下Promise

Promise介绍

PromiseJavaScript 异步编程的一种流行解决方案(号称是终极解决方案),掌握 Promise 的使用是js开发者不可或缺的一项基本技能。

ES6 Promise 对象

ES6的Promise对象是一个构造函数,用来生成Promise实例。
Promise 的构造函数必须接收一个函数参数(也就是需要执行异步任务的函数),该函数将在传入以后立即调用,并传入 Promise 对象下的两个方法 resolvereject
所谓Promise对象,就是代表了未来某个将要发生的事件(通常是一个异步操作)。
它的好处在于,有了Promise对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数

  • then 方法

  • then方法的2个参数;onresolove 和 onreject;

  • then的返回值,会返回一个新的 Promise 对象, 但是状态会有几种情况:

    • then 的回调函数中没有返回值,then就会返回一个状态为: resolved 的 promise 对象
    • then 的回调函数返回值是 非 promise 的值, then就会返回一个状态为: resolved 的 promise 对象,另外会把返回值,传递给 下一个 then
    • then 的回调函数返回值是 promise 对象,then 就直接返回这个 promise 对象,具体的状态可以由我们自己定义,具体传递的值,也由我们自己定义
  • Promise 中的方法包括:resolve、reject、all、race、finally…

Promise 状态

  • 每一个Promise对象都有三种状态: pending 、resolve 和 reject
    • PENDING : 进行中,Promise 对象的初始状态
    • FULFILLED : 已成功
    • REJECTED : 已失败
  • 每一个 Promise 对象只能由 PENDING 状态变成 FULFILLEDREJECTED,且状态发生变化以后就能再改变了
  • 一个 Promise 对象状态的变化并不由 Promise 对象本身来决定,而应该是由我们传入的异步任务完成情况来决定的,Promise 提供了resolve, reject两个用来改变状态的方法

Promise中的静态方法

Promise.resolve 方法

Promise.resolve() 静态方法将给定的值转换为一个 Promise。如果该值本身就是一个 Promise,那么该 Promise 将被返回;
Promise 对象的状态从 PENDING 变为 FULFILLED,并执行成功后的注册任务
比如下面的代码
在这里插入图片描述

注意:如果当前状态已经改变过了,则直接 return

Promise.reject 方法

Promise.reject() 静态方法返回一个已拒绝(rejected)的 Promise 对象,拒绝原因为给定的参数。
Promise 对象的状态从 PENDING 变为 REJECTED,并执行失败后的注册任务

在这里插入图片描述

注意:如果当前状态已经改变过了,则直接 return

Promise.all方法

Promise.all() 静态方法接受一个 Promise 可迭代对象作为输入,并返回一个 Promise。当所有输入的 Promise 都被兑现时,返回的 Promise 也将被兑现(即使传入的是一个空的可迭代对象),并返回一个包含所有兑现值的数组。如果输入的任何 Promise 被拒绝,则返回的 Promise 将被拒绝,并带有第一个被拒绝的原因

在这里插入图片描述
在这里插入图片描述

Promise.allSettled方法

Promise.allSettled() 静态方法将一个 Promise 可迭代对象作为输入,并返回一个单独的 Promise。当所有输入的 Promise 都已敲定时(包括传入空的可迭代对象时),返回的 Promise 将被兑现,并带有描述每个 Promise 结果的对象数组。
这个方法也是为了弥补Promise.all方法中如果有某个promise被拒绝了,就没办法获取所有值的一个缺点
在这里插入图片描述

Promise.race方法

Promise.race() 静态方法接受一个 promise 可迭代对象作为输入,并返回一个 Promise。这个返回的 promise 会随着第一个 promise 的敲定而敲定。
在这里插入图片描述

Promise.any方法

Promise.any() 静态方法将一个 Promise 可迭代对象作为输入,并返回一个 Promise。当输入的任何一个 Promise 兑现时,这个返回的 Promise 将会兑现,并返回第一个兑现的值。当所有输入 Promise 都被拒绝(包括传递了空的可迭代对象)时,它会以一个包含拒绝原因数组的 AggregateError 拒绝。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

Promise实例的方法

promise.then方法

thenPromise 对象提供的一个方法,它接收两个函数作为参数,分别注册到 resolvereject 方法执行后的任务队列中,后面我们自己实现一个JS版本的Pormise,需要维护这两个队列

  • fulfilledQueues
  • rejectedQueues

结果传递

resolvereject之后就会执行对应的注册任务队列,可以通过传入一些值,在后续的 then 方法中,可以通过对应的函数接收到该结果

注意:Promise.then方法是一个微任务,Promise.catch也是微任务而Promise中的方法是同步任务

返回值

then 方法在执行最后必须返回一个新的 Promise 对象,其返回值像上面说的一样,一般有三种情况

  • then 的回调函数中没有返回值,then就会返回一个状态为: resolved 的 promise 对象
  • then 的回调函数返回值是 非 promise 的值, then就会返回一个状态为: resolved 的 promise 对象,另外会把返回值,传递给 下一个 then
  • then 的回调函数返回值是 promise 对象,then 就直接返回这个 promise 对象,具体的状态可以由我们自己定义,具体传递的值,也由我们自己定义

例子说明

const p = new Promise((resolve, reject) => {
	setTimeout(()=> resolve('val'), 1000)
})
// 注意,这里的p1,p2,p3是并行调用而非链式调用
// 第一次调用then
const p1 = p.then(val=>{
  console.log('success1',val);
  return 'p1-val'
}, err=>console.log('err1', err));

// 第二次调用then,then中没有返回值undefine
const p2 = p.then(val=>console.log('success2',val), err=>console.log('err2', err))

// 第三次调用then
const p3 = p.then(val=>{
  console.log('success3',val);
  // 返回一个promise对象
  return Promise.resolve('promise中的值')
}, err=>console.log('err1', err));

p1.then(val => console.log('p1的第二个then函数', val))
p2.then(val => console.log('p2的第二个then函数', val))
p3.then(val => console.log('p3的第二个then函数', val))

比如上述代码,p是一个Promise的实例,这个实例p中先是调用了三次then方法(p1, p2, p3),那对应到具体的逻辑是

  1. 第一次在调用then方法时传递的两个函数val=>{ console.log('success1',val); return 'p1-val'err=>console.log('err1', err)分别注册到fulfilledQueuesrejectedQueues队列中
  2. 第二次调用then方法时传递的两个函数v=>console.log('sucess2',val)err=>console.log('err2', err)再次push注册到fulfilledQueuesrejectedQueues队列中
  3. 第三次调用then方法时传递的两个函数val=>{ console.log('success3',val); return Promise.resolve('promise中的值')err=>console.log('err2', err)再次注册到fulfilledQueuesrejectedQueues队列中
  4. 1s中之后触发了resolve,会取到p实例中的fulfilledQueues函数数组并把val作为参数传递给队列中的函数依次执行(如果是调用了reject就会调用p示例中的rejectedQueues队列执行其中的注册函数)对应的效果就是打印出sucess1 valsucess2 valsuccess3 val
  5. 后面执行p1, p2, p3第二次调用的方法,我们可以看到第一次p1中调then方法返回了一个非promise的普通值,那就会把这个普通值包装成Promise返回给下一个then,所以下一个then接受到的就是’p1-val’
  6. 第一次p2中调用的then方法没有返回值,也会封装成一个undefined的Promise对象。所以p2的第二个then函数接受到的值就是undefine
  7. 第一次p3中调用的then方法返回的是一个Promise对象,就会直接把这个Promise的结果(这里是resolve状态中的’promise中的值’)返回给下一个then
    所以整体打印结果如下

在这里插入图片描述
需要注意的是,在Promise 构造函数中,返回的Promise是resolve中的值,而不是return的值、比如下面这一串代码

const p = new Promise((resolve, reject) => {
  resolve(1)
  console.log(3);
  return 2
})
console.log(p);

p.then(val => console.log(val))

在Promise构造函数中,resolve的值是1,所以传递给下一个then的值就是1,或者是Promise构造函数的返回的promise实例的值是1,而不是return 2。我们习惯性的以为函数的返回值都是rerturn来的,但这里有点和我们的常识不一致。Promise构造函数值是看resolve中的值而非return
而在后续then中的返回值会成为返回promise的值,进而传递给下一个then

const p = new Promise((resolve, reject) => {
  resolve(1)
  return 2
})
console.log(p);

p.then(val=> {
  console.log('接受来自Promise构造函数实例处Promise的值:', val)
  return '返回的promise的值'
}).then(val => console.log(val))

在这里插入图片描述

promise.catch方法

Promise 对象的 catch() 方法用于注册一个在 promise 被拒绝时调用的函数。它会立即返回一个等价的 Promise 对象,这可以允许你链式调用其他 promise 的方法。此方法是 Promise.prototype.then(undefined, onRejected) 的一种简写形式。
比如下面代码

const promise = new Promise((resolve, reject) => {
  setTimeout(() => reject('出错啦'), 1000)
})

promise.then(undefined, error => console.log('catch err in then:', error))

promise.catch(error => console.log('catch err:', error))

在这里插入图片描述

promise.finally方法

finally() 方法返回一个 Promise。在 promise 结束时,无论结果是 fulfilled 或者是 rejected,都会执行指定的回调函数。这为在 Promise 是否成功完成后都需要执行的代码提供了一种方式。这避免了同样的语句需要在 then() 和 catch() 中各写一次的情况。

const promise = new Promise((resolve, reject) => {
  setTimeout(() => resolve('okk'), 3000)
})

promise
  .then(val => console.log('success:', val), err => console.log('err', err))
  .finally(() => {
    // 不管promise成功还是失败,都会执行
    // 由于无法知道状态,所以不接受任何参数,仅用于无论最终结果如何都要执行的情况
    console.log('promise finally')
  })

在这里插入图片描述
在这里插入图片描述

参考文档

Promise-catch
Promise-finally

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

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

相关文章

常见面试题之线程中并发锁(一)

1. 讲一下synchronized关键字的底层原理? 1.1. 基本使用 如下抢票的代码,如果不加锁,就会出现超卖或者一张票卖给多个人 Synchronized【对象锁】采用互斥的方式让同一时刻至多只有一个线程能持有【对象锁】,其它线程再想获取这…

常州工学院计算机组成原理(样卷)

微程序控制器:仿照程序设计的基本方法,将实现指令系统中所有指令所需要的所有控制信号按照一定的规则编码成微指令,若干条实现同一条指令功能的微指令构成一段微程序,将实现所有指令的微程序存放在一个只读存储器ROM中&#xff0c…

一套A股量化系统

shares A 股量化交易系统后台开发语言 Go/Python gmsec算法使用:pytorch全链路量化,行业板块分析,直接贴图。欢迎体验

【计算机视觉】最新综述:南洋理工和上海AI Lab提出基于Transformer的视觉分割综述

文章目录 一、导读二、摘要三、内容解读3.1 研究动机3.2 这篇综述的特色,以及与以往的Transformer综述有什么区别?3.3 Transformer-Based 分割和检测方法总结与对比3.4 相关研究领域的方法总结与对比3.5 不同方法的实验结果对比3.6 未来可以进行的方向 一…

vue三种方式导出报表至excel

1、序言 1.1、源码 源码在下方,复制运行,安装相应的插件即可 1.2、坑 方式一、方式二安装相同插件: npm install xlsx xlsx-style file-saver 导入 xlsx-style 会报如下的错误 解决办法: (1)去node_modules…

多元回归预测 | Matlab基于灰狼算法(GWO)优化混合核极限学习机HKELM回归预测, GWO-HKELM数据回归预测,多变量输入模型

文章目录 效果一览文章概述部分源码参考资料效果一览 文章概述 多元回归预测 | Matlab基于灰狼算法(GWO)优化混合核极限学习机HKELM回归预测, GWO-HKELM数据回归预测,多变量输入模型 评价指标包括:MAE、RMSE和R2等,代码质量极高,方便学习和替换数据。要求2018版本及以上。 …

基于matlab使用深度学习估计身体姿势(附源码)

一、前言 此示例演示如何使用 OpenPose 算法和预训练网络估计一个或多个人的身体姿势。 身体姿势估计的目标是识别图像中人的位置及其身体部位的方向。当场景中存在多个人时,由于遮挡、身体接触和相似身体部位的接近,姿势估计可能会更加困难。 有两种…

【手撕算法|动态规划系列No.1】leetcode1137. 第 N 个泰波那契数

个人主页:平行线也会相交 欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 平行线也会相交 原创 收录于专栏【手撕算法系列专栏】【LeetCode】 🍔本专栏旨在提高自己算法能力的同时,记录一下自己的学习过程,希望…

【C/C++】数组指针:array 地址 array *parray 两次解引用 **parray 值相同的原因解析

一、提出问题 #include <stdio.h>int main() { char array[16] {A, B}; char (*parray)[16] &array; printf("\n");printf(" array: \t%#lx\n", array); printf("& array: \t%#lx\n", &array); printf("…

SQL server 2012 配置数据库邮件实现邮件发送

日常开发中经常遇到邮件推送场景&#xff0c;我们可以利用SQL server也可以实现邮件发送功能。 一、配置邮件服务器 然后再弹出的页面中选择下一步 输入配置文件名&#xff0c;并添加新用户 在弹出的页面配置邮件服务器的地址、用户名、密码等相关信息 以上信息完成&#xff0…

树莓派 Raspberry Pi Zero 2W 安装默认系统时 ssh 登录并开启摄像头推流一段时间B

Raspberry Pi Zero 2W有点鸡肋&#xff0c;hdmi口用的microhdmi口&#xff0c;不是树莓派4b的minihdmi口&#xff0c;然后zero 2W也没有usb接口&#xff0c;有一个microusb安卓的otg接口&#xff0c;很烦&#xff0c;还好有wifi蓝牙模块&#xff0c;这样子还能ssh&#xff0c;不…

【冒泡排序】

前言 在计算机科学中&#xff0c;排序算法是一种常见且重要的算法。排序算法的目标是将一组无序的数据按照一定的规则进行重新排列&#xff0c;以便更方便地进行搜索、查找或其他操作。 冒泡排序&#xff08;Bubble Sort&#xff09;是最简单的排序算法之一&#xff0c;它的原…

基于Python+MySQL所写的商城管理系统

点击以下链接获取源码资源&#xff1a; https://download.csdn.net/download/qq_64505944/87971437?spm1001.2014.3001.5503 《51商城》程序使用说明 51商城项目分为网站前台和后台两个部分&#xff0c;下面将分别介绍这2个部分的使用。 1.网站前台 在虚拟环境中启动程序后&a…

Callback自定义测试-业务安全测试实操(23)

Callback自定义测试 测试原理和方法 在浏览器中存在着同源策略,所谓同源是指域名、协议、端口相同。当使用Aiax异步传输数据时,非同源域名之间会存在限制。其中有一种解决方法是JSONP (JSONwithPadding),基本原理是利用了HTML里<script></script>元素标签,远程…

Python 对象拷贝的那点事?

1.变量&#xff0c;引用和对象 变量无类型&#xff0c;它的作用仅仅在某个时候引用了特定的对象而已&#xff0c;具体在内存中就是一个指针&#xff0c;仅仅拥有指向对象的空间大小。 变量和对象的关系在于引用&#xff0c;变量引用对象后&#xff0c;也就对应了赋值的过程。…

VBA快速合并数据

实例需求&#xff1a;原始数据保存在工作表的A列至C列&#xff0c;现需要根据材料编号合并交付日期和交付数量&#xff0c;并且交付日期的日期格式采用两位数字年份简写格式&#xff0c;合并后的数据保存在E列和F列&#xff0c;如下图所示。 示例代码如下。 Sub demo()Dim o…

MSP430F249 Proteus仿真数码管秒表-0050

MSP430F249 Proteus仿真数码管秒表-0050 Proteus仿真小实验&#xff1a; MSP430F249 Proteus仿真数码管秒表-0050 功能&#xff1a; 硬件组成&#xff1a;MSP430F249单片机 2位数码管2个按键&#xff08;清零 开始/暂停&#xff09; 1.点击开始键后数码管开始秒表计时0~9…

Spring Cloud - HTTP 客户端 Feign 、自定义配置、优化、最佳实践

目录 一、Feign 是什么&#xff0c;有什么用呢&#xff1f; 二、Feign 客户端的使用 2.1、远程调用 1.引入依赖 2.在order-service&#xff08;发起远程调用的微服务&#xff09;的启动类添加注解开启Feign的功能 3.编写 Feign 客户端 4.通过 Feign 客户端发起远程调用 …