JavaScript - 异步编程

news2024/9/27 19:26:42

1. 前言

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

2. 异步函数

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

异步的本质是因为底层机制:像 setTimeoutPromiseasync/await 等等。这些机制让 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. 回调函数

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

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

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

function huidiao(introduce)
{
    let name = 'Bob';
    introduce(name); //在此函数中调用
};

function introduce(name)
{
    console.log(name);
};
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. 箭头函数

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

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

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

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

5. Promise类型

5.1 创建Promise

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类型。

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返回值

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

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

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

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 链的末尾,它就可以在任何异步函数失败时被调用。于是,我们就可以将一个操作实现为几个连续的异步函数调用,并在一个地方处理所有错误。

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是一个列表。

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

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

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语法糖可以更加方便快捷的去创建异步函数

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

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

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

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的

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

7. 小例子:闹钟警报器

7.1 基于setTimeout函数

<!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

<!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 以防止继续执行后面的代码
			}

			// 设置定时器来触发 resolve
			window.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 函数用于标记异步操作的成功完成,并且可以传递成功的结果。

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.coloradmin.cn/o/2137621.html

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

相关文章

河海大学《2020年+2021年827自动控制原理真题》 (完整版)

本文内容&#xff0c;全部选自自动化考研联盟的&#xff1a;《25届河海大学827自控考研资料》的真题篇。后续会持续更新更多学校&#xff0c;更多年份的真题&#xff0c;记得关注哦~ 目录 2020年复试真题 2021年初试真题 Part1&#xff1a;完整版真题 2020年复试真题 2021年…

Cortex-A7:__disable_irq和GIC_DisableIRQ、__enable_irq和GIC_EnableIRQ的区别(1)API介绍

0 相关资料 ARM Generic Interrupt Controller Architecture version 2.0.pdf 1 API介绍 1.1 __disable_irq __disable_irq函数的作用是失能IRQ&#xff0c;也就是不响应中断。实现代码如下&#xff1a; /**\brief Disable IRQ Interrupts\details Disables IRQ interrupt…

Pikachu靶场之XSS

先来点鸡汤&#xff0c;少就是多&#xff0c;慢就是快。 环境搭建 攻击机kali 192.168.146.140 靶机win7 192.168.146.161 下载zip&#xff0c;pikachu - GitCode 把下载好的pikachu-master&#xff0c;拖进win7&#xff0c;用phpstudy打开网站根目录&#xff0c;.....再用…

Python基础知识学习(2)

一&#xff0c;分支条件判断语句 在python中&#xff0c;分支条件语句如下&#xff1a; 1&#xff0c;判断条件&#xff1a;if exception: 2&#xff0c;接着判断的语句为&#xff1a;elif exception: 3&#xff0c;最后的条件为&#xff1a;else: 4&#xff0c;通过缩进来表示…

建一栋房子会用到哪些资质?

在建筑工程领域&#xff0c;资质是衡量建筑企业实力和能力的重要标准。修建一栋房子&#xff0c;从设计到施工&#xff0c;再到最终的验收和维护&#xff0c;涉及到众多环节和专业领域。每个环节都需要相应的资质作为保障&#xff0c;以确保工程的质量和安全。那么&#xff0c;…

Jsp学习笔记(详解)

千锋教育Java视频 从入门到精通 JSP 4h 一&#xff0c;引言 1.1 现有问题 在之前学习Servlet时&#xff0c;服务端通过Servlet响应客户端页面&#xff0c;有什么不足之处&#xff1f; 开发方式麻烦&#xff1a;继承父类、覆盖方法、配置Web.xml或注解代码修改麻烦&#xff1a;…

移除元素(反向双指针)

题目&#xff1a; 算法分析&#xff1a; 优化关键&#xff1a;题目不需要关注除前k元素以外的部分左指针等于对应值&#xff0c;复制右指针替代&#xff0c;右指针增加&#xff08;左指针不变可以继续判断复制的右指针元素是否符合条件&#xff09;否则左指针增加 算法图解&a…

华为项目管理培训产品总监兼首席架构师刘钊受邀为第四届中国项目经理大会演讲嘉宾

全国项目经理专业人士年度盛会 华为项目管理培训产品总监兼首席架构师刘钊先生受邀为PMO评论主办的全国项目经理专业人士年度盛会——2024第四届中国项目经理大会演讲嘉宾&#xff0c;演讲议题为“打造项目管理人才队伍——华为项目经理任职资格和专业认证体系”。大会将于10月…

unity3d入门教程五

unity3d入门教程五 13鼠标事件处理13.2鼠标跟随13.3鼠标拖拽&#xff08;选中对象&#xff0c;拖动对象&#xff09;13.4几个问题14.1事件函数14.2脚本的执行顺序14.3脚本的参数14.4引用类型的参数&#xff08;进行图片更换&#xff0c;人物换装&#xff09; 13鼠标事件处理 需…

PointNet++改进策略 :模块改进 | PointCAT, 使用交叉注意力机制来提升3D点云任务中提升模型精度

论文题目&#xff1a;PointCAT:Cross-Attention Transformer for Point Cloud通讯地址&#xff1a;南京理工大学代码地址&#xff1a;https://github.com/xincheng-yang/PointCAT . PointCAT架构&#xff1a;PointCAT提出了一种基于交叉注意力机制的Transformer网络&#xff0c…

【Python机器学习】序列到序列建模——使用序列到序列网络构建一个聊天机器人

为了寻聊天机器人&#xff0c;下面使用康奈尔电影对话语料库训练一个序列到序列的网络来“适当的”湖大问题或语句。以下聊天机器人示例采用的是Keras blog中的序列到序列的示例。 为训练准备语料库 首先&#xff0c;需要加载语料库并从中生成训练集&#xff0c;训练数据将决…

项目升级必备!TS装饰:简化代码、增加功能的利器 | TypeScript入门指南07

嘿&#xff0c;朋友&#xff01;听说过TS里的装饰器没&#xff1f;就像给代码加了个‘魔法贴’&#xff0c;轻轻一点&#xff0c;功能升级&#xff0c;结构清晰。这篇文章&#xff0c;咱们聊聊这背后的魔法是怎么一回事&#xff01; ts 入门指南系列 Ts vs Js 谁适合前端开发&a…

用Inno Setup打包QT程序输出安装包

InnoSetup打包编译好的QT程序 文章目录 InnoSetup打包编译好的QT程序介绍具体步骤自定义脚本更改引入配置文件/动态库路径申请管理员权限设置安装过程界面的图标和图片C程序依赖运行库 介绍 Inno Setup&#xff1a;用于打包安装程序 具体步骤 首先打开inno setup compiler 第…

MES系统:智能工厂与数字化改造的关键引擎

一、概念范畴&#xff1a; 数字化工厂&#xff1a;是一个更广泛的概念&#xff0c;它强调整个制造过程的数字化转型&#xff0c;包括从产品设计、生产准备、制造过程、管理到营销等各个环节的数字化。数字化工厂利用信息技术、自动化技术和智能化技术对传统工厂进行改造和升级…

怎样在备忘录中添加提醒?怎么设置备忘录提醒

备忘录作为我们日常生活中常用的软件&#xff0c;其记录事项的便捷性已经得到了广泛认可。无论是工作计划、购物清单还是个人日记&#xff0c;备忘录都能帮助我们将这些信息快速记录下来。然而&#xff0c;如果备忘录能够进一步提供提醒功能&#xff0c;那么它将变得更加实用&a…

为什么要用docker?

目录 一、为什么会有docker出现 二、docker理念 三、容器与虚拟机的差异 四、开发/运维&#xff08;DevOps&#xff09; 五、企业级应用 一、为什么会有docker出现 一款产品从开发到上线&#xff0c;从操作系统&#xff0c;到运行环境&#xff0c;再到应用配置。作为开发…

pytorch快速入门(一)—— 基本工具及平台介绍

前言 该pytorch学习笔记应该配合b站小土堆的《pytorch深度学习快速入门教程》使用 环境配置&#xff1a;Anaconda Python编译器&#xff1a;pycharm、jupyter 两大法宝函数 dir&#xff08;&#xff09;&#xff1a;知道包中有什么东西&#xff08;函数 / 属性..…

串口数据波形显示工具对比

目录 1 Serial Port Plotter 1.1 界面 1.2 简介 1.3 使用方法 1.3.1 单通道示例 1.3.2 多通道示例 2 serialplot 2.1 界面 2.2 简介 2.3 使用方法 1 Serial Port Plotter 1.1 界面 1.2 简介 这是一款开源串口显示工具&#xff0c;项目链接&#xff1a; GitHub - C…

待机模式中WKUP上升沿模拟开机与关机

本篇博客重点在于标准库函数的理解与使用&#xff0c;搭建一个框架便于快速开发 目录 前言 待机模式 代码 wkup.h wkup.c main.c 使用注意 前言 建议先阅读下面的博客中待机模式部分。本博客主要分享代码-基于待机模式WKUP引脚的上升沿实现类似长按开机与关机的功能…

TikTok云手机解决运营效率低、封号问题

TikTok&#xff0c;一个拥有30亿下载量、10亿活跃用户的全球热门平台&#xff0c;已成为众多出海商家的首选运营阵地。其玩法与国内抖音相似&#xff0c;为运营者提供了熟悉的操作环境&#xff0c;降低了上手难度。然而&#xff0c;随着TikTok的快速发展&#xff0c;运营者们也…