JavaScript - 异步编程

ops/2024/9/20 1:17:12/ 标签: javascript, 开发语言, ecmascript, 异步, 算法

1. 前言

在 JavaScript 中,异步编程是一种处理需要等待操作(如网络请求、文件读取或计时器)的编程方式。由于 JavaScript 是单线程的,意味着它一次只能执行一个任务。异步编程允许你在等待某些操作完成时,继续执行其他任务而不会阻塞主线程。

2. 异步函数

异步函数,当你调用它时,它不会立即阻塞代码的执行。程序会立刻继续执行下面的代码。

异步的本质是因为底层机制:像 setTimeoutPromiseasync/await 等等。这些机制让 JavaScript 不需要等待耗时操作的结果,而可以继续执行其他代码,回调函数是在异步操作完成后触发的。

javascript">console.log("I'm going to make dinner");
setTimeOut(()=>{Console.log('My dinner is ready');},1000);
console.log('I'm going to watch TV');//I'm going to make dinner
//I'm going to watch TV
//My dinner is ready

3. 回调函数

回调函数是一个作为参数传递给另一个函数的函数,目的是在特定操作完成后执行。这是一种在异步编程中处理操作完成的方式。

将回调函数看作一个参数,在另一个函数中进行调用就可以很好的去理解。

异步操作中,我们可以将回调函数当做一个通知,异步函数调用完毕后,通知操作者

javascript">function huidiao(introduce)
{let name = 'Bob';introduce(name); //在此函数中调用
};function introduce(name)
{console.log(name);
};
javascript">function doStep1(init, callback) {const result = init + 1;callback(result);
}
function doStep2(init, callback) {const result = init + 2;callback(result);
}
function doStep3(init, callback) {const result = init + 3;callback(result);
}
function doOperation() {doStep1(0, (result1) => {doStep2(result1, (result2) => {doStep3(result2, (result3) => {console.log(`结果:${result3}`);});});});
}
doOperation();

doOperation 函数依次调用doStep1doStep2doStep3,并在每一步完成后,将结果传递给下一步的回调函数。

在上述代码中,(result1) ==> {} 表示回调函数,在JavaScript独有的箭头函数

4. 箭头函数

javascript">(result) => {// 函数体
}

其中result代表函数所接受的参数

{}中运行的结构自动作为返回值
eg:

javascript">const add = (a, b) => a + b;
console.log(add(2, 3)); // 输出:5

5. Promise类型

5.1 创建Promise

javascript">const promise = new Promise((resolve, reject) => {// 异步操作if (/* 操作成功 */) {resolve('成功的结果');} else {reject('失败的原因');}
});

使用我们的异步函数,最后异步函数所返回的类型为Promise类型

首先,Promise 有三种状态:

  • 待定(pending):初始状态,既没有被兑现,也没有被拒绝。这是调用 fetch() 返回 Promise 时的状态,fetch()是一个异步函数,不干扰主线程,此时请求还在进行中。
  • 已兑现(fulfilled):意味着操作成功完成。当 Promise 完成时,then() 处理函数(类似于我们的回调函数,异步任务处理完成后)被调用。
  • 已拒绝(rejected):意味着操作失败。当一个 Promise 失败时,它的 catch() 处理函数被调用。catch函数进行处理错误。

5.2 fetch()函数

使用fetch异步函数,会得到Promise返回对象

Promise返回对象:Promise { <state>: "pending" }。这告诉我们有一个 Promise 对象,它有一个 state属性,值是 "pending""pending" 状态意味着操作仍在进行中。所有的异步函数返回对象都是Promise类型。

javascript">const fetchPromise = fetch("https://mdn.github.io/learning-area/javascript/apis/fetching-data/can-store/products.json",
);console.log(fetchPromise);fetchPromise.then((response) => {console.log(`已收到响应:${response.status}`);
});console.log("已发送请求……");

上述代码使用fetch函数,将内容获取操作不占用主线程,仍然可以继续运行主线程的代码。

5.3 .then

获取到返回的Promise对象之后,使用Promise对象参数then,类似于我们的回调函数,异步操作完成之后我们需要干什么的编写。

promise.then会判断异步操作成功后,自动向回调函数传入response返回值

javascript">fetchPromise.then((response) => response.json()).then((data) => {console.log(data[0].name);});

与response所结合完成我们的异步操作

上述代码所示,两个then也是同步所进行的。

javascript">const fetchPromise = fetch("https://mdn.github.io/learning-area/javascript/apis/fetching-data/can-store/products.json",
);fetchPromise.then((response) => {const jsonPromise = response.json();jsonPromise.then((json) => {console.log(json[0].name);});
});

5.4 .catch

你调用它并传入一个处理函数。然后,当异步操作成功时,传递给 then() 的处理函数被调用,而当异步操作失败时,传递给 catch() 的处理函数被调用。 

如果将 catch() 添加到 Promise 链的末尾,它就可以在任何异步函数失败时被调用。于是,我们就可以将一个操作实现为几个连续的异步函数调用,并在一个地方处理所有错误。

javascript">const fetchPromise = fetch("bad-scheme://mdn.github.io/learning-area/javascript/apis/fetching-data/can-store/products.json",
);fetchPromise.then((response) => {if (!response.ok) {throw new Error(`HTTP 请求错误:${response.status}`);}return response.json();}).then((json) => {console.log(json[0].name);}).catch((error) => {console.error(`无法获取产品列表:${error}`);});

5.5 .final

无论获取是正确还是错误,异步操作仍然执行的部分。 

5.6 Promise.all()

处理多个异步函数时可以用到。

当所有的异步函数处理完毕后,使用Promise.all(异步函数返回对象),对所有的返回对象做进一步处理,通常搭配循环,返回的responses是一个列表。

注意:处理过程是同步运行过程

当有一个异步未完成时,抛出错误。

javascript">const fetchPromise1 = fetch("https://mdn.github.io/learning-area/javascript/apis/fetching-data/can-store/products.json",
);
const fetchPromise2 = fetch("https://mdn.github.io/learning-area/javascript/apis/fetching-data/can-store/not-found",
);
const fetchPromise3 = fetch("https://mdn.github.io/learning-area/javascript/oojs/json/superheroes.json",
);Promise.all([fetchPromise1, fetchPromise2, fetchPromise3]).then((responses) => {for (const response of responses) {console.log(`${response.url}:${response.status}`);}}).catch((error) => {console.error(`获取失败:${error}`);});

6. async&await关键字

使用async语法糖可以更加方便快捷的去创建异步函数

在函数内部也是通过同步的形式进行运行,但与主线程的工作不相互冲突。

javascript">async function OK()
{//异步函数
}

定义好异步函数之后,可以在转换线程的代码前声明await,这使得代码在该点上等待,直到 Promise 被完成,这时 Promise 的响应被当作返回值,或者被拒绝的响应被作为错误抛出。

javascript">async function time_end
{try {const response = await fetch('url');}const json = response.json();catch(error){console.error(`${error}`)};
}

await fetch所返回的是一个完整的response对象而不是promise对象。

await相当于自动将Promise对象转为response对象

await 强制异步操作以串联的方式完成。如果下一个操作的结果取决于上一个操作的结果,这是必要的,但如果不是这样,像 Promise.all() 这样的操作会有更好的性能。

异步函数中使用循环

注意:在异步函数中是不能使用foreach的

javascript">async function yibu()
{    const promises = [program1(),program2(),program3()];for wait (let I of promises){}
}

7. 小例子:闹钟警报器

7.1 基于setTimeout函数

javascript"><!DOCTYPE html>
<html><head><meta charset="utf-8"><title></title></head><body><button id='set-alarm'>Set alarm</button><div id='output'></div></body><script>const output = document.querySelector('#output');const button = document.querySelector('#set-alarm');function setAlarm(){window.setTimeout(()=>{output.textContent = 'Wake up';},2000);}button.addEventListener("click", setAlarm);</script>
</html>

7.2 基于async&await

javascript"><!DOCTYPE html>
<html><head><meta charset="utf-8"><title></title></head><body>Name: <input type='text' id='name'><br>Delay:  <input type='number' id='delay'><button id='set-alarm'>Set alarm</button><div id='output'></div></body><script>const output = document.querySelector('#output');const button = document.querySelector('#set-alarm');const delay = document.querySelector('#delay');const name = document.querySelector('#name');function alarm(person, delay) {return new Promise((resolve, reject) => { //函数返回一个Promise对象if (delay < 0) {reject(new Error('Alarm delay must not be negative'));return;  // 需要 return 以防止继续执行后面的代码}// 设置定时器来触发 resolvewindow.setTimeout(() => {resolve(`Wake up, ${person}!`);}, delay);  // 注意这里没有括号错位});}button.addEventListener('click', async () => {try {const message = await alarm(name.value, parseInt(delay.value));  // 确保 delay.value 是数字output.textContent = message;} catch (error) {output.textContent = error.message;  // 如果发生错误,显示错误信息}});</script>
</html>

7.3 resolve参数

通常与reject参数一起搭配使用

它是 Promise 对象的构造函数中的一个参数。resolve 的作用是将 Promise 对象的状态从 待定(pending) 变为 已解决(fulfilled)。这意味着 resolve 函数用于标记异步操作的成功完成,并且可以传递成功的结果。

javascript">const myPromise = new Promise((resolve, reject) => {// 模拟异步操作setTimeout(() => {const success = true; // 假设操作成功if (success) {resolve('操作成功'); // 将 Promise 状态变为已解决,并传递结果} else {reject('操作失败'); // 将 Promise 状态变为已拒绝,并传递错误}}, 1000);
});

8. 参考资料

如何实现基于 Promise 的 API - 学习 Web 开发 | MDN


http://www.ppmy.cn/ops/112577.html

相关文章

【原创】java+swing+mysql长途客车售票管理系统设计与实现

个人主页&#xff1a;程序员杨工 个人简介&#xff1a;从事软件开发多年&#xff0c;前后端均有涉猎&#xff0c;具有丰富的开发经验 博客内容&#xff1a;全栈开发&#xff0c;分享Java、Python、Php、小程序、前后端、数据库经验和实战 文末有本人名片&#xff0c;希望和大家…

C#泛型(Generic)

泛型&#xff08;Generic&#xff09;允许延迟编写类或方法中的编程元素的数据类型的规范&#xff0c;直到实际在程序中使用它的时候。换句话说&#xff0c;泛型允许编写一个可以与任何数据类型一起工作的类或方法。 可以通过数据类型的替代参数编写类或方法的规范。当编译器遇…

【JavaScript】数据结构之堆

对数据结构像树&#xff0c;但是&#xff0c;是通过数组来实现的&#xff08;不是通过链表&#xff09;

基于SSM的在线家用电器销售系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、SSM项目源码 系统展示 【2025最新】基于JavaSSMVueMySQL的在线家…

Docker部署Joplin Server教程

Joplin Server 是 Joplin 应用的后端服务,提供笔记和待办事项的同步功能。它允许用户在不同设备之间同步笔记,同时支持多用户和协作功能。Joplin Server使用现代技术栈,数据库使用的是 PostgreSQL 。 主要功能 同步:在桌面、移动设备和网页应用之间同步笔记。多用户支持:允…

【梯度消失|梯度爆炸】Vanishing Gradient|Exploding Gradient——为什么我的卷积神经网络会不好呢?

【梯度消失|梯度爆炸】Vanishing Gradient|Exploding Gradient——为什么我的卷积神经网络会不好呢&#xff1f; 【梯度消失|梯度爆炸】Vanishing Gradient|Exploding Gradient——为什么我的卷积神经网络会不好呢&#xff1f; 文章目录 【梯度消失|梯度爆炸】Vanishing Gradi…

【限流算法】常见的限流算法有哪些,怎么做限流操作

【限流算法】常见的限流算法有哪些&#xff0c;怎么做限流操作 在Java应用中实现限流&#xff08;Rate Limiting&#xff09;通常是为了控制对资源或服务的访问速率&#xff0c;防止因过载而导致的服务不可用。Java中实现限流的方法有多种&#xff0c;以下是一些常见的方法&…

Spring Boot- 配置文件问题

Spring Boot 配置文件问题探讨 Spring Boot 是目前主流的 Java 开发框架之一&#xff0c;其核心特性之一便是“约定优于配置”&#xff08;Convention over Configuration&#xff09;。在此基础上&#xff0c;Spring Boot 提供了灵活而强大的配置文件机制&#xff0c;帮助开发…

HTML5好看的水果蔬菜在线商城网站源码系列模板2

文章目录 1.设计来源1.1 主界面1.2 商品列表界面1.3 商品详情界面1.4 其他界面效果 2.效果和源码2.1 动态效果2.2 源代码 源码下载 作者&#xff1a;xcLeigh 文章地址&#xff1a;https://blog.csdn.net/weixin_43151418/article/details/142059220 HTML5好看的水果蔬菜在线商城…

【佳学基因检测】在EXCEL中,如何获取A列的第9-29个字符,将其填入另一列中

【佳学基因检测】在EXCEL中&#xff0c;如何获取A列的第9-29个字符&#xff0c;将其填入另一列中 在 Excel 中&#xff0c;如果你需要从 A 列的单元格中提取第 9 到第 29 个字符&#xff0c;你可以使用 MID 函数来实现。这是一个非常实用的函数&#xff0c;用于从文本字符串中…

基于kolla-ansible在AnolisOS8.6上部署all-in-one模式OpenStack-Train

测试环境 Anolis OS8.6 Virtual Box&#xff0c;4 vCPU, 8G RAM, 50 vDisk。安装时删除/home&#xff0c;SWAP分区&#xff0c;全部空间给/目录。 目标是部署OpenStack All-In-One模式&#xff0c;控制节点计算节点存储节点在一台机器实现。 系统配置 常用工具 dnf install …

C语言--结构体(学习笔记)

内容借鉴于b站杜远超官方频道&#xff08;C语言结构体详解【干货】&#xff09; 首先C语言中定义变量格式为“数据类型 变量名”&#xff0c;如int a; float b;等等。 那么结构体则是将多个变量&#xff08;数据类型 变量名&#xff09;结合在一起的一种新的数据类型&…

C++掉血迷宫

目录 开头程序程序的流程图程序游玩的效果下一篇博客要说的东西 开头 大家好&#xff0c;我叫这是我58。 程序 #include <iostream> #include <string> #include <cstring> using namespace std; enum RBYG {R 1,B 2,Y 4,G 7, }; struct heal {int ix…

四川锦程消费金融有限责任公司2024年(第一批次)催收机构选型入库采购公告

四川锦程消费金融有限责任公司2024年&#xff08;第一批次&#xff09;催收机构选型入库采购公告 根据需要&#xff0c; 四川锦程消费金融有限责任公司决定对外公 开选型采购催收代理合作的催收机构&#xff0c;欢迎符合条件的催收 机构参与采购。具体公告如下&#xff1a; 一…

如何在Linux下升级R版本和RStudio

一、升级R版本 在Linux上&#xff0c;R的安装通常通过包管理器完成。不同的Linux发行版&#xff08;如Ubuntu、Debian、Fedora等&#xff09;可能略有不同。下面以Ubuntu为例&#xff0c;介绍如何升级R版本。如果你使用其他发行版&#xff0c;步骤可能类似。 二.更新步骤 2.…

UE5安卓项目打包安装

Android studio安装 参考&#xff1a;https://docs.unrealengine.com/5.2/zh-CN/how-to-set-up-android-sdk-and-ndk-for-your-unreal-engine-development-environment/ 打开android studio的官网&#xff1a;Download Android Studio & App Tools - Android Developers …

a,b,c中的最大值

题目描述 编写一个程序&#xff0c;输入a、b、c三个值&#xff0c;输出其中最大值。 输入格式 一行三个整数&#xff0c;分别为 a、b、c。 输出格式 a、b、c 中的最大值。 输入数据 1 10 20 30输出数据 1 30数据范围与提示 数字 a、b、c 都在 int 范围内。 代码 #i…

【网络安全】分享4个高危业务逻辑漏洞

未经许可,不得转载。 文章目录 正文逻辑漏洞1逻辑漏洞2逻辑漏洞3逻辑漏洞4其它正文 该目标程序是一家提供浏览器服务的公司,其核心功能是网页抓取和多账户登录操作,类似于浏览器中的隐身模式,但更加强大和高效。通过该平台,用户可以轻松管理并同时运行数百个隐身浏览器实…

初探全同态加密1 —— FHE的定义与历史回顾

文章目录 一、加密体系1、什么是加密体系2、加密体系的属性 Properties 二、同态加密&#xff1a;偶然的特殊性质三、同态加密体系的分类四、部分同态加密 Partially Homomorphic Encryption1、加法同态加密算法 —— ElGamal 加密算法1.1、ElGamal 的大致步骤1.2、ElGamal 的加…

永久配置清华源,告别下载龟速

为了每次使用 pip 时自动使用清华源&#xff0c;可以通过以下步骤进行配置&#xff1a; 方法一&#xff1a;通过命令行配置 你可以在命令行中运行以下命令来自动设置清华源&#xff1a; pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple此命令会将…