《无所不能的JavaScript · 异步编程》

news2024/12/23 8:36:53

📢 大家好,我是 【战神刘玉栋】,有10多年的研发经验,致力于前后端技术栈的知识沉淀和传播。 💗
🌻 CSDN入驻不久,希望大家多多支持,后续会继续提升文章质量,绝不滥竽充数,欢迎多多交流。👍

文章目录

    • 写在前面的话
    • JavaScript 异步编程
      • 技术简介
      • 回调地狱
      • Promise
      • async、await
      • 其他用法
    • 总结陈词

CSDN.gif

写在前面的话

异步编程允许我们在执行一个长时间任务时,程序不需要等待,而是继续执行之后的代码,直到任务完成后再通知你,通常是以回调函数的形式。
这种编程模式,避免了程序的阻塞,提高了CPU的执行效率,用户体验得到了提升。
以 Java 中异步编程为例,其用法丰富多彩,可以利用JUC等各种工具方法实现多线程效果,以此提升系统系统,比如下面示例代码。
自定义一个 Callable 接口,利用线程池提交,返回 Future 对象,通过调用 get 方法阻塞等待结果。

Future<?> future = executorService .submit(new MyCallable());
System.out.println("结果为:" + future.get());

回到正题,接下来介绍一下 JavaScript 中异步编程的运用,让我们开始。


JavaScript 异步编程

技术简介

JavaScript从设计之初就是一个单线程的编程语言,浏览器无论在什么时候都有且只有一个线程在运行JavaScript程序,即同一时间只执行一条代码,所以每一个JavaScript代码执行块会“阻塞”其它异步事件的执行。
但依然不能阻止JavaScript实现异步编程效果,来一段示例代码:

setTimeout(() => {
  console.log('Hello World')
}, 1000)

console.log('123')

很明显,先输出123,再输出Hello World,这里可以看一下之前的这篇博文:《setTimeout 简笔》
JavaScript的单线程的异步编程方式其实有诸多优点,由于所有操作都运行在同一个线程中,无须考虑线程同步和资源竞争的问题,从源头上避免了线程之间的频繁切换,降低了线程自身的开销。


回调地狱

JS 中常见的异步场景,除了 setTimeout,还有 AJAX、Axios、Fetch 等远程数据获取,都是利用回调函数实现异步效果。
回调函数虽然方便,但它有一个明显的缺点。
如果我们需要依次执行多个异步操作,代码可能变成下面这样,典型的“向右编程”,可读性和可控性都很差。

setTimeout(() => {
  console.log('Hello World')
  setTimeout(() => {
    console.log('Hello World')
    setTimeout(() => {
      console.log('Hello World')
    },1000)   
  },1000)
},1000)

这种情况也被叫做函数的“回调地狱”。
为了解决这个问题,Promise应运而生。


Promise

Promise 是ES6引进的关键,它一个对象,代表了一个异步操作的最终完成(或失败)及其结果。它允许你为异步操作的结果提供一个回调函数,而不是使用传统的回调函数嵌套。

Promise 的创建:
使用 new Promise() 构造函数,并提供一个执行器函数(executor),这个函数接受两个参数:resolve 和 reject。resolve 用于解决(fulfill)Promise,而 reject 用于拒绝(reject)Promise。

Promise 的状态:
pending(等待态)、fulfilled(完成态)、rejected(拒绝态)。

Promise 的方法:
.then() 用于处理 Promise 成功的结果,.catch() 用于处理 Promise 失败的结果,.finally() 用于在 Promise 成功或失败后执行一些操作。

Promise 的示例:

// 使用new Promise方式改写,意义其实不大,这个本身其实就是
// 这段代码手动创建了一个Promise,并在其构造函数中提供了执行函数,这个函数接收两个参数:resolve和reject。这两个函数分别用于在异步操作成功或失败时调用。
// 当this.$axios.get('/api/hello')成功返回响应时,我们调用resolve并传递响应数据。如果发生错误,我们调用reject并传递错误对象。
// 然后,你可以链式调用.then()来处理成功的结果,以及.catch()来处理任何可能出现的错误。这种方式允许你以更传统的方式处理异步操作
// 但它通常没有使用async/await那么简洁和直观。
let sayHello = new Promise((resolve, reject) => {
  this.$axios.get('/api/hello')
      .then(response => {
        resolve(response);
      })
      .catch(error => {
        reject(error);
      });
})
sayHello.then(response => {
  console.log('使用new Promise方式改写,成功:' + response.data)
}).catch(error => {
  console.log('使用new Promise方式改写,失败:' + error)
});

Tips:上面都是一些理论,接下来用企业开发常用的 Fetch 技术加以说明。

常用的fetchAxios都是基于Promise的,这意味着它们都使用 Promise 来处理异步操作。
浏览器直接输入fetch函数,可以看到返回值是一个 Promise 对象,Promise 字面意思是承诺,承诺这个请求会在未来某个时刻返回数据。
image.png
那是怎么解决回调地域的?可以看下面示例代码,Promise 的链式调用避免了代码的层层嵌套,很明显不再是“向右编程”,而是“向下编程”。

fetch('http://127.0.0.1:8082/api/hello')
  .then(response => {return response.json()})
  .then(data => { console.log("get.data:", data)})
  .catch(error => {console.log("get.error:", error.message)})
  .finally(() => {console.log("get.finally")})

//输出:get.data: Hello, Spring Boot 2!
//输出:get.finally

如果在链式调用的时候,遇到错误,可以用catch,如果之前任意一个阶段报错,都会被catch捕捉,后续流程则不执行。
同时可以添加finally做兜底动作,无论失败与否都会执行,这里面可以执行 Loading 动画关闭等事情。
其实和 Java 的try-catch-finally代码块差不多,链式调用就有点像 Java8 的 Stream。
好吧,再次证明语言是互通的,条条大路通罗马。


async、await

async 和 await 是 ES2017(ES8)中引入的,它们基于 Promise 提供了更简洁的语法来编写异步代码。
简单来说,它们是基于 Promise 之上的一个语法糖,可以让异步操作更加简单明了。

【基础使用】
如下代码,函数添加了 async 代表是异步函数,内部利用 await 可以阻塞该操作,等待结果的返回。
这里的 this.$axios.get返回的是一个 Promise 对象,await 通常搭配其使用,当然也可以跟同步代码,都会阻塞。
注意,此时如果函数外有其他逻辑,不会被阻塞。

async fetchData() {
  try {
    const response = await this.$axios.get('/api/sleep');
    this.data = response.data;
  } catch (error) {
    this.error = error.message;
  }
},

【补充说明】
并不是 async 函数就必须和 await 一起使用。你可以声明一个 async 函数,但完全不使用 await 关键字。在这种情况下,函数体会同步执行,并且函数会直接返回一个状态为 fulfilled 的 Promise,其值为函数的返回值。
但是,await 关键字只能在 async 函数内部使用。这意味着你不能在非 async 函数中使用 await。

【再一个示例】


// 定义一个返回 Promise 的异步函数
function fetchData() {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve("data");
    }, 1000);
  });
}

// 使用 async 和 await 的函数
async function fetchDataAsync() {
  const data = await fetchData(); // 等待 fetchData() 解决并获取结果
  console.log(data); // 'data'
}

// 调用异步函数
fetchDataAsync();

在这段代码中,fetchData 返回一个 Promise,而 fetchDataAsync 是一个异步函数,它等待 fetchData 的 Promise 解决,然后打印结果。
使用 async 和 await 可以使异步代码看起来更像是同步代码,从而简化了异步编程的复杂性。


其他用法

【Promise.all】
如果 async 函数中,有多个异步请求逻辑,使用多个 await 关键词会导致效率较低。
此时可以通过 Promise.all 的方式改造,具体参考下面代码,效率提升接近一倍。

async fetchData() {
  
  // 创建两个Promise对象
  let handleOne = this.$axios.get('/api/hello');
  let handleTwo = this.$axios.get('/api/sleep');
  
  // Promise.all方法改写
  // 等数组中的所有promise对象都完成执行
  const [a, b] = await Promise.all([handleOne, handleTwo]);
  console.log(a)
  console.log(b)
}

还有一个更酷炫的写法,for await,也会等所有操作都执行完才向后执行。

async fetchData() {
  
  let handleOne = this.$axios.get('/api/hello');
  let handleTwo = this.$axios.get('/api/sleep');
  
  let asyncArr = [handleOne, handleTwo]
  for await (let result of asyncArr) {
    console.log(result)
  }

  console.log('done')
}

【Promise几种常用方法】
1、Promise.all()
将多个Promise封装成一个新的Promise,成功时返回的是一个结果数组,失败时,返回的是最先rejected状态的值。
使用场景:一次发送多个请求并根据请求顺序获取和使用数据。
2、Promise.race()
返回一个 promise,一旦迭代器中的某个promise解决或拒绝,返回的 promise就会解决或拒绝。
简单来说,就是多个Promise中,哪个状态先变为成功或者失败,就返回哪个Promise的值。
3、Promise.any()
接收一个Promise可迭代对象,只要其中的一个 promise 成功,就返回那个已经成功的 promise 。如果可迭代对象中没有一个 promise 成功(即所有的 promises 都失败/拒绝),就返回一个失败的 promise和AggregateError类型的实例,它是 Error 的一个子类,用于把单一的错误集合在一起。本质上,这个方法和Promise.all()是相反的。
4、Promise.allSettled()
Promise.allSettled()方法返回一个在所有给定的promise都已经fulfilled或rejected后的promise,并带有一个对象数组,每个对象表示对应的promise结果。
当您有多个彼此不依赖的异步任务成功完成时,或者您总是想知道每个promise的结果时,通常使用它。
相比之下,Promise.all() 更适合彼此相互依赖或者在其中任何一个reject时立即结束。

【关于 async 和 await 的使用】
并不是 async 函数就必须和 await 一起使用。你可以声明一个 async 函数,但完全不使用 await 关键字。在这种情况下,函数体会同步执行,并且函数会直接返回一个状态为 fulfilled 的 Promise,其值为函数的返回值。
但是,await 关键字只能在 async 函数内部使用。这意味着你不能在非 async 函数中使用 await。

// 不使用 await 的 async 函数
async function synchronousFunction() {
  // 这里没有使用 await
  return "This is synchronous";
}

synchronousFunction().then(console.log); // "This is synchronous"

// 使用 await 的 async 函数
// await 使得 asynchronousFunction 在等待 Promise 解决之前不会继续执行。这提供了异步操作的一种更加直观和易于理解的方式。
async function asynchronousFunction() {
  const result = await new Promise(resolve => {
    setTimeout(() => {
      resolve("This is asynchronous");
    }, 1000);
  });
  return result;
}

asynchronousFunction().then(console.log); // "This is asynchronous"(在1秒后打印)

总结陈词

JavaScript 中的 Promise、await 和 async 是处理异步操作的关键概念和关键字,它们在处理异步编程时起着至关重要的作用。
让我们继续见证了 JavaScript 的博大精深,后续会更新更多内容。
💗 如果觉得内容还可以,麻烦点个关注不迷路,您的鼓励是我创作的动力。

CSDN_END.gif


参考链接:
阮一峰 - ES6
使用ES6的Promise完美解决回调地狱
Javascript异步编程的4种方法
Promise和async/await

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

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

相关文章

数据库:编程(打开、操作、关闭)

一、需要的头文件 sqlite3.h 二、编译过程 gcc xxx -lsqlite3 三、编程框架 打开数据库 》读写数据库(增&#xff0c;删&#xff0c;改&#xff0c;查) 》关闭数据库 3.1 打开数据库&#xff1a; sqlite3_open int sqlite3_open(char * path,sqlite3 ** db); 功能&…

【入门篇】2.3 STM32启动模式(一)

一,Boot引脚分步 二,启动电路 三,启动模式 STM32F4 根据 BOOT 引脚的电平选择启动模式,这两个 BOOT 引脚根据外部施加的电平来决定芯片的启动地址。 下表中 BOOT0 和 BOOT1 是 STM32 芯片上面的两个引脚,用于控制 STM32

【Elasticsearch】Elasticsearch 中的节点角色

Elasticsearch 中的节点角色 1.主节点&#xff08;master&#xff09;1.1 专用候选主节点&#xff08;dedicated master-eligible node&#xff09;1.2 仅投票主节点&#xff08;voting-only master-eligible node&#xff09; 2.数据节点&#xff08;data&#xff09;2.1 内容…

引起全球大规模蓝屏故障的CrowdStrike是什么,到底发生了什么?|TodayAI

在周五早晨&#xff0c;一场由网络安全公司CrowdStrike引发的大规模故障使全球范围内的多家大型航空公司、电视广播公司、银行和其他重要服务陷入停顿。此次故障导致无数Windows机器出现“蓝屏死机”&#xff08;BSOD&#xff09;&#xff0c;系统无法正常启动。据悉&#xff0…

畅玩游戏新选择 :游戏本 Windows10 64位 专业版!

对于喜欢游戏竞技的玩家而言&#xff0c;选择一款合适的操作系统对于提升游戏体验至关重要。为了满足这一需求&#xff0c;系统之家小编将带来高性能的游戏本专用Win10操作系统。这一版本系统不仅注重游戏的稳定性&#xff0c;还针对玩家在游戏中可能遇到的超时检测和恢复&…

如何处理 PostgreSQL 中死锁的情况?

&#x1f345;关注博主&#x1f397;️ 带你畅游技术世界&#xff0c;不错过每一次成长机会&#xff01;&#x1f4da;领书&#xff1a;PostgreSQL 入门到精通.pdf 文章目录 如何处理 PostgreSQL 中死锁的情况&#xff1f;一、认识死锁二、死锁的症状三、死锁的检测四、预防死锁…

C++基础知识:引用的基本使用

1.作用&#xff1a; 给变量别名 2.语法&#xff1a; 数据类型 &别名原名 3.图例解释&#xff1a; 4.代码解释&#xff1a; #include<iostream>using namespace std;int main(){//引用基本语法int a10;int &ba;//两者公用了空间b和a都可以改变//该空间内的数…

一分钟图情论文:《智慧数据驱动的古籍智慧性保护体系研究》

中山大学的陈涛、苏日娜等人合著的《智慧数据驱动的古籍智慧性保护体系研究》论文探讨了智慧数据在古籍保护中的应用&#xff0c;提出了古籍智慧性保护体系框架。 智慧数据是一个动态的变量&#xff0c; 而非静态的常量&#xff0c;具有定性评价、流变发展和多态组成三个特点古…

Getx学习笔记之中间件鉴权

目录 前言 一、实现步骤 1.添加依赖 2.创建鉴权中间件 3.定义路由 4.设置初始路由 5.模拟登陆状态 二、Getx鉴权步骤总结 三、本文demo示例 四、参考文章 前言 在 Flutter 中&#xff0c;使用 GetX 可以很方便地实现中间件鉴权&#xff08;Authentication&#xff09…

Ubuntu编译ffmpeg并添加cmake工程

文章目录 前言前提须知为什么要自己编译 FFmpeg前提软件包与工具的安装编译ffmpeg写CMakeList.txt包含ffmpeg到我们项目中 总结 前言 FFmpeg 是一个领先的多媒体框架&#xff0c;能够解码、编码、转码、复用、解复用、流化、过滤和播放几乎所有人类和机器创造的内容。FFmpeg 包…

视频监控平台LntonCVS视频融合共享平台智慧安防视频监控汇聚应用方案

LntonCVS是一款功能强大且灵活部署的安防视频监控平台。它支持多种主流标准协议&#xff0c;包括GB28181、RTSP/Onvif、RTMP等&#xff0c;同时能够兼容海康Ehome、海大宇等厂家的私有协议和SDK接入。该平台不仅提供传统的安防监控功能&#xff0c;还支持接入AI智能分析&#x…

解读vue3源码-响应式篇2

提示&#xff1a;看到我 请让我滚去学习 文章目录 vue3源码剖析reactivereactive使用proxy代理一个对象1.首先我们会走isObject(target)判断&#xff0c;我们reactive全家桶仅对对象类型有效&#xff08;对象、数组和 Map、Set 这样的集合类型&#xff09;&#xff0c;而对 str…

月之暗面科技有限公司(Moonshot AI)内推

月之暗面科技有限公司&#xff08;Moonshot AI&#xff09;内推 公司简介 月之暗面科技有限公司&#xff0c;一家致力于实现通用人工智能&#xff08;AGI&#xff09;的创新企业&#xff0c;其主力产品 Kimi 智能助手&#xff0c;以其超长文本处理能力和多端同步功能&#xff…

215. 数组中的第K个最大元素 347. 前 K 个高频元素(LeetCode热题100)

215. 数组中的第K个最大元素 - 力扣&#xff08;LeetCode&#xff09; 写个快排&#xff0c;使数组升序&#xff0c;返回倒数第k个元素即可 func quickSort(nums []int, l int, r int) {if l > r {return}x : nums[(l r) / 2]i : l - 1j : r 1for i < j {for {iif n…

【HTML入门】第十六课 - 网页中的按钮们

这一小节&#xff0c;我们说一说 html网页 中的按钮。按钮的作用&#xff0c;就是让用户点呗&#xff0c;点了以后触发一些事件&#xff0c;可以做一些事情。 目录 1 input 按钮 一 2 input 按钮二 3 button 按钮 4 链接a标签 5 用其他元素做按钮 6 用图片做一个按钮 1 …

为什么要从C语言开始编程

在开始前刚好我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「C语言的资料从专业入门到高级教程」&#xff0c; 点个关注在评论区回复“888”之后私信回复“888”&#xff0c;全部无偿共享给大家&#xff01;&#xff01;&#xff01;很多小伙伴在入门编程时。都…

Python学习—open函数,json与pickle知识点,Os模块详解

目录 1. Open函数 2.json与pickle模块 json模块 1. json.dumps() 2. json.dump() 3. json.loads() 4. json.load() pickle 模块 1. pickle.dumps() 2. pickle.dump() 3. pickle.loads() 4. pickle.load() 3.Os模块 1. Open函数 在Python中&#xff0c;open() 函数…

【大数据】JSON文件解析,对其文本聚类/情感分析

目录 引言 JSON&#xff08;JavaScript Object Notation&#xff09; 文本聚类K-means 基本步骤 优点 缺点 实际应用 情感分析 核心任务与应用场景 算法原理与技术 json数据集 情感分析实现 文本聚类实现 引言 JSON&#xff08;JavaScript Object Notation&#…

AIoTedge 智能边缘物联网平台

AIoTedge智能边缘物联网平台是一个创新的边云协同架构&#xff0c;它为智能设备和系统提供了强大的数据处理和智能决策能力。这个平台的核心优势在于其边云协同架构设计&#xff0c;它优化了数据处理速度&#xff0c;提高了系统的可靠性和灵活性&#xff0c;适用于多种场景&…

华为OD机考题(HJ61 放苹果)

前言 经过前期的数据结构和算法学习&#xff0c;开始以OD机考题作为练习题&#xff0c;继续加强下熟练程度。 描述 把m个同样的苹果放在n个同样的盘子里&#xff0c;允许有的盘子空着不放&#xff0c;问共有多少种不同的分法&#xff1f; 注意&#xff1a;如果有7个苹果和3…