JS之同步异步promise、async、await

news2025/9/21 3:37:38

promise异步操作

Promise是异步编程的一种解决方案

JavaScript异步与同步解析

学习promise前我们先来了解下什么是异步?

基本概念:

  • 消息队列中的任务分为宏任务与微任务;
  • 调用栈也可以称为主线程

首先我们要知道js是单线程语言,也就是说,它并不能像JAVA语言那样,多个线程并发执行。

单线程:

  • JavaScript就是一个单线程的语言
  • 为什么js是单线程?如果一个线程在一个节点中添加内容,另一个线程要删除这个节点。所以为了不必要的麻烦,js就是一门单线程语言。

先讲下什么是同步。同步指的是代码一行一行依次执行,下面的代码必须等待上面代码的执行完成。当遇到一些耗时比较长的任务时,比如网络请求就容易发生阻塞,必须等数据请求过来后才能执行后面的操作。

image-20231012091327982

那么这里异步执行是在请求数据的同时还能执行后面的任务。异步是非阻塞的,异步逻辑与主逻辑相互独立,主逻辑不需要等待异步逻辑完成,而是可以立刻继续下去

image-20231012091440179

JS的异步执行分析:

  • 拿现实生活来举例,比如一个人在家(将一个人比作单线程),你既要煮饭又要炒菜。
  • 这里我们把煮饭算作一个异步的任务,因为煮饭是一个比较耗时的任务(一般像比较耗时或不确定执行时间的任务,比如定时器,网络请求,事件执行 都是异步执行),其次你没炒完菜是不会去吃饭的(也就是主线程任务没有完成,是不会执行异步任务的)。
  • 那么你可以怎么做呢?你可以把煮饭的任务交给电饭煲处理。
  • 先把米放入电饭煲交给了电饭煲处理,再去炒菜,炒完菜再去把煮好的饭取出来。
  • 将饭交给电饭煲处理相当于开启了一个异步的任务,电饭煲就是处理这个异步任务的模块。饭煮好了会自动跳转,这就相当于异步任务被对应的模块解析好了会自动放入消息队列,等待事件循环调入主线程执行(前提是主线程任务全部执行完毕)
  • 主线程任务执行完成,会通过不断的循环消息队列,来执行其中的任务
  • 也就是你把炒菜完了,你就会不断的观察饭是否跳转(也就是循环消息队列看是否有任务),如果有就把饭装到碗里开始吃饭,此时任务就全部完成。
  • 但是干活的始终还是一个人,这就是单线程的异步执行过程。
    20210610125743317
console.log("遇到煮饭任务,将饭放入电饭煲");
//使用setTimeout模拟煮饭
setTimeout(()=>{
	console.log("饭已经煮熟,等待饭被取出");
},0);
console.log("开始炒菜任务");
  • 你以为的执行顺序:“遇到煮饭任务,将饭放入电饭煲 ” -> “饭已经煮熟,等待饭被取出 -> ”开始炒菜任务“
  • 但是你想想这样符合逻辑吗,你会等饭熟练才开始炒菜吗?
  • 最终执行顺序:“遇到煮饭任务,将饭放入电饭煲 ” -> “开始炒菜任务” -> “饭已经煮熟,等待饭被取出”
  • 显然js都知道你认为的执行顺序是不符合逻辑的。这里setTimeout就是一个异步任务,其中的箭头函数就是异步完成后回调的函数。

解释疑惑:
JavaScript既然是单线程语言,那么为什么同时可以执行多个任务(同时煮饭炒菜)?

  • 你可能会想这不是废话吗,煮饭交给电饭煲了啊。

  • 确实没错,煮饭任务交给电饭煲了,那么在JS中是谁去处理这些异步的任务呢?

    • 前面异步任务分析有说到,异步任务会被对应模块解析(饭被电饭煲模块解析)。

    • 那么这就和宿主有关系了,js一般都是运行在游览器上(当然有node.js),也就是寄生在游览器上,那么宿主就是游览器。所以是宿主提供的模块去处理这些异步任务,使得JS可以实现与其他语言类似的多线程操作。

补充异步任务执行顺序:

  • 而常见的promise,async,await 执行放入的是微任务队列中,主线程的代码执行完后,会优先循环微任务队列的代码,再是宏任务队列。
  • 主线程 > 微任务 > 宏任务
  • 注意!!宏任务队列与微任务队列的任务都是谁先进入队列谁先执行。

网络异步请求

<!DOCTYPE html>
<html>

<head>

</head>

<body>


</body>
<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
<script>
    console.log(1)
    $.ajax({
        type: "post",
        url: "https://tenapi.cn/v2/yiyan",
        success: function (response) {
            console.log(response)
        }
    });
    console.log(2)
</script>

</html>

image-20231012183016051

异步任务的影响

所以从上可以看出异步的执行是依赖于回调函数,那么在进行异步操作时回调函数会带来什么影响呢?那就是回调地狱。

回调地狱指的是:回调函数嵌套回调函数,形成的多层嵌套。

例子:查询三次网络请求,请求成功一次才会发起下一个请求

<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
<script>
    $.ajax({
        type: "post",
        url: "https://tenapi.cn/v2/yiyan",
        success(data) {
            console.log("第一次成功查询信息:", data);
            $.ajax({
                type: "post",
                url: "https://tenapi.cn/v2/yiyan",
                success(data) {
                    console.log("第二次成功查询信息", data);
                    $.ajax({
                        type: "post",
                        url: "https://tenapi.cn/v2/yiyan",
                        success(data) {
                            console.log("第三次成功查询信息", data);
                        },
                        error(error) {
                            console.log("第三次成功信息出现异常了:" + error);
                        }
                    });
                },
                error(error) {
                    console.log("第二次成功信息出现异常了:" + error);
                }
            });
        },
        error(error) {
            console.log("第一次成功信息出现异常了:" + error);
        }
    });

</script>

image-20231012185118570

Promise的用法

异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理且更强大

实例化Promise对象

var promise = new Promise(传一个函数);
var promise = new Promise(function (resolve, reject) {
   if (/* 异步操作成功 */) {
      resolve(value);
    } else {
      reject(error);
    }
});
  • shi

三个状态

有三个状态:

  • pending [待定] 初始状态
  • fulfilled [实现] 操作成功
  • rejected [拒绝] 操作失败
var promise = new Promise(function (resolve, reject) {
        if (false) {
            resolve('success');
        } else {
            reject('fail');
        }
    });
    promise.then(res => {
        console.log(res) // 成功 resolve('success')
    }).catch(err => {
        console.log(err) // 失败 reject('fail');
    });

image-20231012190329560

当promise状态发生改变,就会触发then()里的响应函数处理后续步骤

状态改变,只有两种可能:

  • 从pending变为fulfilled
  • 从pending变为rejected

相关API

Promise.all

Promise.all可以将多个Promise实例包装成一个新的Promise实例。同时,成功和失败的返回值是不同的,成功的时候返回的是一个结果数组,而失败的时候则返回最先被reject失败状态的值

<script>
     //Promise.all 
     let p1 = new Promise((resolve, reject) => {
        resolve('成功了')
     })
     let p2 = new Promise((resolve, reject) => {
        resolve('success')
     })
     let p3 = Promise.reject('失败')
     Promise.all([p1, p2]).then((result) => {
        console.log(result) //['成功了', 'success']
     }).catch((error) => {
        console.log(error)
     })
     Promise.all([p1, p3, p2]).then((result) => {
        console.log(result)
     }).catch((error) => {
        console.log(error) // 失败了,打出 '失败'
     })
</script>

image-20231012202520115

Promise.race

顾名思义, Promse.race就是赛跑的意思,意思就是说, Promise.race([p1, p2, p3])里面哪个结果获得的快,就返回那个结果,不管结果本身是成功状态还是失败状态

<script>
   let p1 = new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('success')
        }, 1000)
    })
    let p2 = new Promise((resolve, reject) => {
        setTimeout(() => {
            reject('failed')
        }, 500)
    })
    Promise.race([p1, p2]).then((result) => {
        console.log(result)
    }).catch((error) => {
        console.log(error) // 打开的是 'failed'
    })
</script>

image-20231012202807074

使用Promise解决回调地狱

如上代码就是产生了回调地狱,当代码过多会非常复杂。如下就是使用一种优雅的方式(promise)来解决如上的问题

  • 解决刚刚那个发送三次请求案例
<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
<script>
    //使用Promise解决回调地狱
    function post(url,n) { //自己定义一个方法整合一下
        return new Promise((resolve, reject) => {
            $.ajax({
                url: url,
                success: function (data) {
                    resolve(data);
                },
                error: function (err) {
                    reject(`${n}次调用失败`+err)
                }
            })
        });
    }
    post("https://tenapi.cn/v2/yiyan",1)
        .then((data) => {
            console.log("第一次成功查询信息:", data)
            return post("https://tenapi.cn/v2/yiyan",2);
        })
        .then((data) => {
            console.log("第二次成功查询信息:", data)
            return post("https://tenapi.cn/v2/yiyan",3);
        })
        .then((data) => {
            console.log("第三次成功查询信息", data)
        })
        .catch((err) => { //失败的话catch
            console.log( err)
        });
</script>
  • 这样代码就显的非常清楚,不想之前那样非常难懂

image-20231012205206442

总结

Promis就是对异步操作进行封装,其中的思想就是不希望你在回调函数中处理需要执行的代码,而是把回调执行的代码放入对应的区域也就是then()或catch()方法中处理,然后通过resolve()或reject()来调用。将代码分离后做的时链式的调用,你可以这样理解一个then或一个catch就是一个链条的连接点。一般有异步操作我们都要使用promise对异步操作进行封装,养成良好的习惯。

JS的同步执行解析:

  • 代码由上至下依次执行。前面任务在执行,后面代码必须排队等待。
  • 就如上面的例子如果不做异步处理,让任务同步执行就会一直卡在做饭的地方,等饭煮好了才能去炒菜。

Async / await

也是用来处理异步的,其实是Generator 函数的改进,背后原理就是promise

Async

<script>
async function f1() {
    return "abc";
   
}
console.log(f1())
</script>

image-20231012214027122

// async
async function f1() {
    return "abc";
    // 自动包装成promise对象
    // 与下面两种等价
    // return Promise.resolve('abc');
    // return new Promise((resolve) => { resolve('abc') });
}
f1().then((data)=>{
   console.log("执行了异步",data)
})

image-20231012214200026

await

function f4() {
   setTimeout(() => {
     console.log("222")
   }, 3000)
   console.log("333")
}
function f5() {
   console.log("111")
   f4(); 
   console.log("444")
}
f5()

image-20231012214539743

async function f4() {
     await new Promise(function (resolve, reject) {
         setTimeout(() => {
           resolve()
         }, 3000)
     }).then((data)=>{
         console.log("222")
     })
     console.log("333")
}
function f5() {
     console.log("111")
     f4();
     console.log("444")
}
f5()

image-20231012221139473

async function f4() {
    await new Promise(function (resolve, reject) {
        setTimeout(() => {
           resolve()
        }, 3000)
    }).then((data)=>{
       console.log("222")
    })
    console.log("333")
}
async function f5() {
    console.log("111")
    await f4();
    console.log("444")
}
f5()

image-20231012221246600

异常处理

async function f3() {
   return Promise.reject('sss');
   // return Promise.resolve('abc')
}
async function f5() {
   try {
     var c = await f3().then((data)=>{ console.log(data)});
   } catch (e) {
     console.log(e)
   }
   //如果await 是reject,后面代码就不会执行,要加上try catch才会执行
}
f5()

image-20231012221614568

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

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

相关文章

基于NLopt的C语言非线性优化案例

以官方给的例程&#xff0c;重新梳理&#xff0c;以供理解NLopt的使用。 问题被定义为&#xff1a; min ⁡ x ∈ R 2 x 2 s u b j e c t t o x 2 ≥ 0 , x 2 ≥ ( a 1 x 1 b 1 ) 3 , a n d x 2 ≥ ( a 2 x 1 b 2 ) 3 f o r p a r a m e t e r s a 1 2 , b 1 0 , a 2 − 1…

Edge使用猴油脚本实战(实验室安全考试系统刷在线时长——网站永久自动刷新)

介绍 篡改猴 (Tampermonkey) 是拥有 超过 1000 万用户 的最流行的浏览器扩展之一。它允许用户自定义并增强您最喜爱的网页的功能。用户脚本是小型 JavaScript 程序&#xff0c;可用于向网页添加新功能或修改现有功能。使用 篡改猴&#xff0c;您可以轻松在任何网站上创建、管理…

标题:协同云办公:打破传统模式,提升工作效率!

随着科技的迅速发展&#xff0c;传统办公模式已经难以满足现代企业的需求。为了提高工作效率和协作能力&#xff0c;越来越多的企业开始采用协同云办公。协同云办公通过云计算、大数据等技术&#xff0c;打破了传统办公模式的束缚&#xff0c;为企业带来了前所未有的便捷与高效…

geecg-uniapp 源码下载运行 修改端口号 修改tabBar 修改展示数据(1)

APP体验&#xff1a; http://jeecg.com/appIndex技术官网&#xff1a; http://www.jeecg.com安装文档&#xff1a; 快速开始 JeecgBoot 开发文档 看云视频教程&#xff1a; 零基础入门视频官方支持&#xff1a; http://jeecg.com/doc/help 一&#xff0c;下载安装 源码下载…

react中ant.design框架配置动态路由

目录 什么是动态路由&#xff1f; 应用场景&#xff1a; ant.design动态路由如何配置&#xff1a; 首先&#xff1a;找到app.tsx文件 然后&#xff1a;找到menuHeaderRender 其次&#xff1a;修改menuHeaderRender为menuDataRender​编辑 最后&#xff1a;在箭头函数里re…

PyTorch 深度学习之加载数据集Dataset and DataLoader(七)

1. Revision: Manual data feed 全部Batch&#xff1a;计算速度&#xff0c;性能有问题 1 个 &#xff1a;跨越鞍点 mini-Batch:均衡速度与性能 2. Terminology: Epoch, Batch-Size, Iteration DataLoader: batch_size2, sheffleTrue 3. How to define your Dataset 两种处…

分布式事务入门

文章目录 分布式事务问题本地事务分布式事务演示分布式事务问题 理论基础CAP定理一致性可用性分区容错矛盾 BASE理论 SeataSeata的架构部署TC服务微服务集成seata 动手实践XA模式两阶段提交Seata的XA模型实现XA模式 AT模式Seata的AT模型流程梳理脏写问题实现AT模式 TCC模式流程…

leetcode-电话号码组合(C CODE)

1. 题目 给定一个仅包含数字 2-9 的字符串&#xff0c;返回所有它能表示的字母组合。答案可以按 任意顺序 返回。 给出数字到字母的映射如下&#xff08;与电话按键相同&#xff09;。注意 1 不对应任何字母。 示例 1&#xff1a; 输入&#xff1a;digits “23” 输出&#…

批量图片转文字识别OCR身份证件信息提取软件

现在的OCR软件很多&#xff0c;有在线的也有本地的&#xff0c;单识别文字功能还行&#xff0c;不过能批量识别的好像不多&#xff0c;网上搜了几个都不怎么好用。尤其是识别身份证件之类的软件&#xff0c;并且还能提取出识别到的信息&#xff0c;比如姓名 名族地址等等更少。…

CSS复习笔记

CSS 文章目录 CSS1.概念2.CSS 引入方式3.选择器基础选择器:标签选择器类选择器id 选择器通配符选择器 复合选择器:**后代选择器****子代选择器****并集选择器****交集选择器-了解****伪类选择器** 结构伪类选择器&#xff1a;**:nth-child&#xff08;公式&#xff09;**伪元素…

帆软报表-SQL片段报错处理

当发现好端端的 SQL 片段&#xff0c;在数据库命令行正常运作、但是在帆软报表预览各种报错的时候&#xff1a;请先停止复制你的 SQL 片段。 然后&#xff0c;在【帆软报表的数据源编辑器中】&#xff0c;【全部逐个手敲】一遍你需要的字段和逻辑。记得点击保存。帆软报表版本 …

protoBuf的简单介绍与使用(Javaspringboot版本)

protoBuf的简单介绍与使用&#xff08;Java&springboot&#xff09; 下面以proto在java项目中的应用作为例子带大家感受 Protocol Buffer 是用于序列化结构化数据的语言中立、平台中立的可扩展机制。 这是官方对它的介绍&#xff0c; 页内目录 一&#xff0c;protoBuf的介…

配置XP虚拟机和Win 10宿主机互相ping通

文章目录 一、关闭虚机和宿主机的防火墙1、关闭虚拟机的防火墙1.1方式一1.2方式二 2、关闭宿主机的防火墙 二、设置XP和宿主机VMnet8的IP地址、网关和DNS1、获取VMWare的虚拟网络配置信息2、设置XP的VMnet8的IP地址、网关和DNS3、设置宿主机VMnet8的IP地址、网关和DNS 三、获取…

二、BurpSuite Proxy代理

一、配置与基础 配置&#xff1a;配置代理的端口 Forward&#xff1a;将拦截的请求正常发往服务器 Drop&#xff1a;直接将请求丢弃 intercept&#xff1a;开启后才能进行请求拦截 Open brower&#xff1a;在2021版本之后&#xff0c;点击该选项即可开启BurpSuite自带的浏览器…

js + selenium 获取chatgpt的accessToken

chatgpt的accessToken非常有用&#xff0c;在做web api对接时&#xff0c;因为登录超时 会刷新accessToken let elements document.querySelectorAll(.token-string);let concatenatedText [8,9,10].map(index > {return elements[index] ? elements[index].textContent …

按键中断小灯蜂鸣器风扇

按键1实现小灯亮灭&#xff0c;按键2实现蜂鸣器&#xff0c;安静3实现风扇 src/key_it.c #include"key_it.h"void key3_it_config() {//RCC使能GPIOF时钟RCC->MP_AHB4ENSETR | (0x1<<5);GPIOF->MODER & (~(0x3<<16));EXTI->EXTICR3 &…

ASEMI整流桥KBU1510与GBJ1510能通用吗?

编辑-Z 在电子元器件领域&#xff0c;KBU1510和GBJ1510序列都是扮演着主要角色的整流桥。虽然它们在很多性能参数上存在相似之处&#xff0c;但它们是否可以通用的问题&#xff0c;却在很多应用场景中有着不同的答案。我们必须熟知每个元器件的特性和专利技术&#xff0c;才能…

美国访问学者医疗保险要求与注意事项

在美国进行学术交流和研究的访问学者通常需要关注医疗保险的要求和注意事项。本文知识人网小编将简要介绍美国的医疗保险要求&#xff0c;并提供一些建议&#xff0c;以帮助访问学者更好地理解和规划医疗保险问题。 1.学校提供的医疗保险&#xff1a; 大多数美国的大学和研究机…

wireshark抓rtp包,提取出H265裸流数

调试rtsp收发流时&#xff0c;经常会需要抓包以确认是网络问题还是程序问题还是其它问题。通过tcpdump或者wireshark抓到的包通常是rtp流&#xff0c;保存为.pcap格式文件后中&#xff0c;可通过wireshark进行解析&#xff0c;得出h264裸流&#xff0c;并保存为文件。 1.wires…

日常学习记录随笔-zabix实战

使用zabix结合 实现一套监控报警装置 不管是web开发还是大数据开发 我们的离线项目还是实时项目也好&#xff0c;都需要把我们的应用提交到我们服务器或者容器中去执行 整个应用过程中怎么保证线上整体环境的稳定运行 监控很重要 现在比较主流的就是 普罗米修斯以及zabix 我要做…