【react】在react中async/await一般用来实现什么功能

news2025/3/29 19:52:44

目录

基本概念

工作原理

优点

注意事项

底层原理

实际应用场景

1. 数据获取 (API 请求)

2. 表单提交

3. 异步状态管理

4. 异步路由切换

5. 异步数据预加载

6. 第三方 API 调用

7. 文件上传/下载

8. 路由导航拦截

关键注意事项


基本概念

async 函数:用 async 关键字声明的函数会返回一个 Promise。如果函数中返回的不是 Promise,JavaScript 会将其包装成一个 resolved 的 Promise

await 关键字:只能用在 async 函数内部,用于等待一个 Promise 的解决结果。如果等待的不是 Promiseawait 会直接返回该值。

示例代码

// 定义一个异步函数
async function fetchData() {
  try {
    // 模拟一个异步操作,比如网络请求
    const response = await fetch('https://api.example.com/data');
    const data = await response.json(); // 解析响应体为 JSON
    console.log(data);
  } catch (error) {
    console.error('Error fetching data:', error);
  }
}

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

工作原理

async 函数返回一个 Promise

如果函数中返回的是一个普通值,这个值会被包装成一个 resolved 的 Promise

如果函数中返回的是一个 Promise,则直接返回该 Promise

await 暂停函数执行

当遇到 await 时,函数的执行会被暂停,直到等待的 Promise 被解决。

如果等待的不是一个 Promiseawait 会直接返回该值,并继续执行后续代码。

优点

更简洁的代码:相比传统的 Promise 链式调用,async/await 让异步代码看起来像同步代码,逻辑更清晰。

易于调试:错误堆栈更清晰,调试时更容易追踪代码的执行流程。

避免回调地狱:通过 await 逐层等待,避免了嵌套回调的复杂性。

注意事项

只能在 async 函数中使用 await

// 错误:await 不能在普通函数中使用
function normalFunction() {
  const result = await somePromise(); // 语法错误
}

错误处理

async function fetchData() {
  try {
    const response = await fetch('https://api.example.com/data');
    const data = await response.json();
    console.log(data);
  } catch (error) {
    console.error('Error:', error);
  }
}

使用 try/catch 块捕获 await 的错误,而不是 .catch()

并发执行: 如果有多个异步操作需要同时执行,可以使用 Promise.all()

async function fetchMultipleData() {
  const [data1, data2] = await Promise.all([
    fetch('https://api.example.com/data1').then(res => res.json()),
    fetch('https://api.example.com/data2').then(res => res.json())
  ]);
  console.log(data1, data2);
}

底层原理

async/await 的底层是基于 Promise 和事件循环的。当执行一个 async 函数时,函数内部的代码会被包装成一个 Promise,并立即执行。遇到 await 时,当前函数的执行会被暂停,直到等待的 Promise 被解决。解决后,函数会从暂停的地方继续执行。

实际应用场景

1. 数据获取 (API 请求)

从服务器获取数据是最常见的异步操作场景。

import React, { useState, useEffect } from 'react';

const DataFetcher = () => {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    const fetchData = async () => {
      try {
        const response = await fetch('https://api.example.com/data');
        if (!response.ok) {
          throw new Error('Network response was not ok');
        }
        const jsonData = await response.json();
        setData(jsonData);
      } catch (error) {
        setError(error.message);
      } finally {
        setLoading(false);
      }
    };

    fetchData();
  }, []);

  if (loading) return <div>Loading...</div>;
  if (error) return <div>Error: {error}</div>;
  return <div>{JSON.stringify(data)}</div>;
};

export default DataFetcher;

2. 表单提交

处理表单提交时,使用 async/await 等待服务器响应。

import React, { useState } from 'react';

const AsyncForm = () => {
  const [name, setName] = useState('');
  const [email, setEmail] = useState('');
  const [status, setStatus] = useState('idle');

  const handleSubmit = async (e) => {
    e.preventDefault();
    setStatus('submitting');
    try {
      const response = await fetch('https://api.example.com/submit', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({ name, email }),
      });
      if (!response.ok) {
        throw new Error('Failed to submit');
      }
      setStatus('success');
    } catch (error) {
      setStatus(`Error: ${error.message}`);
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        value={name}
        onChange={(e) => setName(e.target.value)}
        placeholder="Name"
      />
      <input
        type="email"
        value={email}
        onChange={(e) => setEmail(e.target.value)}
        placeholder="Email"
      />
      <button type="submit" disabled={status === 'submitting'}>
        {status === 'submitting' ? 'Submitting...' : 'Submit'}
      </button>
      {status === 'success' && <div>Submitted successfully!</div>}
      {status.startsWith('Error') && <div>{status}</div>}
    </form>
  );
};

export default AsyncForm;

3. 异步状态管理

在组件中管理异步状态。

import React, { useState } from 'react';

const AsyncStateExample = () => {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);

  const fetchData = async () => {
    try {
      const response = await fetch('https://api.example.com/data');
      if (!response.ok) {
        throw new Error('Network response was not ok');
      }
      const jsonData = await response.json();
      setData(jsonData);
    } catch (error) {
      console.error('Error fetching data:', error);
    } finally {
      setLoading(false);
    }
  };

  return (
    <div>
      <button onClick={fetchData} disabled={loading}>
        {loading ? 'Loading...' : 'Fetch Data'}
      </button>
      {data && <div>{JSON.stringify(data)}</div>}
    </div>
  );
};

export default AsyncStateExample;

4. 异步路由切换

在使用 React Router 时,完成异步操作后再切换路由。

import React, { useState } from 'react';
import { useHistory } from 'react-router-dom';

const AsyncRouteExample = () => {
  const [username, setUsername] = useState('');
  const history = useHistory();

  const handleLogin = async () => {
    try {
      await new Promise((resolve) => setTimeout(resolve, 1000));
      history.push('/dashboard');
    } catch (error) {
      console.error('Login failed:', error);
    }
  };

  return (
    <div>
      <input
        type="text"
        value={username}
        onChange={(e) => setUsername(e.target.value)}
        placeholder="Username"
      />
      <button onClick={handleLogin}>Login</button>
    </div>
  );
};

export default AsyncRouteExample;

5. 异步数据预加载

在组件渲染前预加载数据。

import React, { useState, useEffect } from 'react';

const AsyncDataPreload = () => {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    const preloadData = async () => {
      try {
        const response = await fetch('https://api.example.com/data');
        if (!response.ok) {
          throw new Error('Network response was not ok');
        }
        const jsonData = await response.json();
        setData(jsonData);
      } catch (error) {
        console.error('Error preloading data:', error);
      } finally {
        setLoading(false);
      }
    };

    preloadData();
  }, []);

  if (loading) return <div>Loading data...</div>;
  return <div>{JSON.stringify(data)}</div>;
};

export default AsyncDataPreload;

6. 第三方 API 调用

与浏览器 API 或第三方 SDK 交互。

async function getGeolocation() {
  return new Promise((resolve, reject) => {
    navigator.geolocation.getCurrentPosition(resolve, reject);
  });
}

function LocationButton() {
  const [position, setPosition] = useState(null);

  const handleClick = async () => {
    try {
      const pos = await getGeolocation();
      setPosition({
        lat: pos.coords.latitude,
        lng: pos.coords.longitude
      });
    } catch (err) {
      alert('无法获取位置信息');
    }
  };

  return (
    <div>
      <button onClick={handleClick}>获取当前位置</button>
      {position && <Map coordinates={position} />}
    </div>
  );
}

7. 文件上传/下载

处理文件上传并跟踪进度。

import axios from 'axios';

function FileUploader() {
  const [progress, setProgress] = useState(0);

  const uploadFile = async (file) => {
    const formData = new FormData();
    formData.append('file', file);

    await axios.post('/api/upload', formData, {
      onUploadProgress: (e) => {
        setProgress(Math.round((e.loaded * 100) / e.total));
      }
    });
  };

  return (
    <div>
      <input type="file" onChange={e => uploadFile(e.target.files[0])} />
      {progress > 0 && <ProgressBar value={progress} />}
    </div>
  );
}

8. 路由导航拦截

防止用户在有未保存更改时离开页面。

import { useNavigate, useLocation } from 'react-router-dom';

function EditArticle() {
  const [isDirty, setIsDirty] = useState(false);
  const navigate = useNavigate();

  useEffect(() => {
    const unblock = navigate.block((tx) => {
      if (isDirty && !window.confirm('有未保存的更改,确定离开?')) {
        tx.retry();
      }
    });
    return () => unblock();
  }, [isDirty]);

  const saveArticle = async () => {
    await fetch('/api/articles', { method: 'PUT' });
    setIsDirty(false);
    navigate('/articles');
  };

  return (/* 编辑器 UI */);
}

关键注意事项

  1. 组件卸载时的处理useEffect 中使用标志位避免组件卸载后的状态更新。

useEffect(() => {
  let isMounted = true;
  
  const fetchData = async () => {
    const data = await fetch('/api/data');
    if (isMounted) setData(data);
  };

  fetchData();
  return () => { isMounted = false; };
}, []);
  1. 错误处理 捕获并处理异步操作中的错误,避免未处理的承诺拒绝。

try {
  await fetchData();
} catch (error) {
  if (error.name !== 'AbortError') {
    showErrorToast(error.message);
  }
}
  1. 防抖优化 对频繁触发的异步操作使用防抖。

const search = useDebouncedCallback(async (query) => {
  const results = await fetchResults(query);
  setResults(results);
}, 500);

通过以上示例和注意事项,可以全面了解在 React 中如何使用 async/await 处理各种异步场景,提升应用的响应性和用户体验。

React 中 async/await 的典型应用场景包括:

  1. 网络请求(GET/POST/PUT/DELETE)

  2. 文件操作(上传/下载)

  3. 浏览器 API 调用(地理位置/摄像头)

  4. 定时任务(延迟执行)

  5. WebSocket 通信

  6. 数据库操作(IndexedDB)

  7. 动画序列控制

  8. 第三方库集成(支付 SDK)

合理使用异步操作可以使:

  • 代码逻辑更清晰(避免回调地狱)

  • 错误处理更直观(try/catch 统一捕获)

  • 用户体验更友好(加载状态/进度提示)

码字不易,各位大佬点点赞呗

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

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

相关文章

Axure项目实战:智慧城市APP(六)市民互动(动态面板、显示与隐藏)

亲爱的小伙伴&#xff0c;在您浏览之前&#xff0c;烦请关注一下&#xff0c;在此深表感谢&#xff01; 课程主题&#xff1a;市民互动 主要内容&#xff1a;动态面板、显示与隐藏交互应用 应用场景&#xff1a;AI产品交互、互动类应用 案例展示&#xff1a; 案例视频&am…

为何服务器监听异常?

报错&#xff1a; 执行./RCF后出现监听异常--在切换网络后&#xff0c;由于前面没有退出./RCF执行状态&#xff1b;重新连接后&#xff0c;会出现服务器监听异常 原因如下&#xff1a; 由于刚开始登录内网&#xff0c;切换之后再重新登录内网&#xff0c;并且切换网络的过程中…

1.认识Excel

一 Excel 可以用来做什么 二 提升技巧 1.数据太多 2.计算太累 3.提升数据的价值和意义 4.团队协作 三 学习目标 学习目标不是为了掌握所有的技能&#xff0c;追逐新功能。而是学知识来解决需求&#xff0c;如果之前的技能和新出的技能都可以解决问题&#xff0c;那不学新技能也…

光谱范围与颜色感知的关系

光谱范围与颜色感知是光学、生理学及技术应用交叉的核心课题&#xff0c;两者通过波长分布、人眼响应及技术处理共同决定人类对色彩的认知。以下是其关系的系统解析&#xff1a; ‌1.基础原理&#xff1a;光谱范围与可见光‌ ‌光谱范围定义‌&#xff1a; 电磁波谱中能被特定…

网络地址转换技术(2)

NAT的配置方法&#xff1a; &#xff08;一&#xff09;静态NAT的配置方法 进入接口视图配置NAT转换规则 Nat static global 公网地址 inside 私网地址 内网终端PC2&#xff08;192.168.20.2/24&#xff09;与公网路由器AR1的G0/0/1&#xff08;11.22.33.1/24&#xff09;做…

Python正则表达式(一)

目录 一、正则表达式的基本概念 1、基本概念 2、正则表达式的特殊字符 二、范围符号和量词 1、范围符号 2、匹配汉字 3、量词 三、正则表达式函数 1、使用正则表达式&#xff1a; 2、re.match()函数 3、re.search()函数 4、findall()函数 5、re.finditer()函数 6…

【TI MSPM0】PWM学习

一、样例展示 #include "ti_msp_dl_config.h"int main(void) {SYSCFG_DL_init();DL_TimerG_startCounter(PWM_0_INST);while (1) {__WFI();} } TimerG0输出一对边缘对齐的PWM信号 TimerG0会输出一对62.5Hz的边缘对齐的PWM信号在PA12和PA13引脚上&#xff0c;PA12被…

MySQL: 创建两个关联的表,用联表sql创建一个新表

MySQL: 创建两个关联的表 建表思路 USERS 表&#xff1a;包含用户的基本信息&#xff0c;像 ID、NAME、EMAIL 等。v_card 表&#xff1a;存有虚拟卡的相关信息&#xff0c;如 type 和 amount。关联字段&#xff1a;USERS 表的 V_CARD 字段和 v_card 表的 v_card 字段用于建立…

更改 vscode ! + table 默认生成的 html 初始化模板

vscode ! 快速成的 html 代码默认为&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>D…

使用LVS的 NAT 模式实现 3 台RS的轮询访问

节点规划 1、配置RS RS的网络配置为NAT模式&#xff0c;三台RS的网关配置为192.168.10.8 1.1配置RS1 1.1.1修改主机名和IP地址 [rootlocalhost ~]# hostnamectl hostname rs1 [rootlocalhost ~]# nmcli c modify ens160 ipv4.method manual ipv4.addresses 192.168.10.7/24…

MySQL实战(尚硅谷)

要求 代码 # 准备数据 CREATE DATABASE IF NOT EXISTS company;USE company;CREATE TABLE IF NOT EXISTS employees(employee_id INT PRIMARY KEY,first_name VARCHAR(50),last_name VARCHAR(50),department_id INT );DESC employees;CREATE TABLE IF NOT EXISTS departments…

华为p10 plus 鸿蒙2.0降级emui9.1.0.228

需要用到的工具 HiSuite Proxy V3 华为手机助手11.0.0.530_ove或者11.0.0.630_ove应该都可以。 官方的通道已关闭&#xff0c;所以要用代理&#xff0c;127.0.0.1端口7777 https://www.firmfinder.ml/ https://professorjtj.github.io/v2/ https://hisubway.online/articl…

C# Modbus RTU学习记录

继C# Modbus TCP/IP学习记录后&#xff0c;尝试串口通信。 操作步骤&#xff1a; 1.使用Visual Studio安装Nuget包NModbus.Serial。 2.使用Modbus Slave应用程序&#xff0c;工具栏Connection项&#xff0c;单击Connect&#xff0c;弹窗Connection Setup&#xff0c;修改Con…

AI+Xmind自动生成测试用例(思维导图格式)

一、操作步骤: 步骤1:创建自动生成测试用例智能体 方式:使用通义千问/豆包智能体生成,以下两个是我已经训练好的智能体,直接打开使用即可 通义智能体: https://lxblog.com/qianwen/share?shareId=b0cd664d-5001-42f0-b494-adc98934aba5&type=agentCard 豆包智能…

(二)手眼标定——概述+原理+常用方法汇总+代码实战(C++)

一、手眼标定简述 手眼标定的目的&#xff1a;让机械臂和相机关联&#xff0c;相机充当机械臂的”眼睛“&#xff0c;最终实现指哪打哪 相机的使用前提首先需要进行相机标定&#xff0c;可以参考博文&#xff1a;&#xff08;一&#xff09;相机标定——四大坐标系的介绍、对…

【Linux网络-NAT、代理服务、内网穿透】

一、NAT技术 1.NAT技术背景 之前我们讨论了&#xff0c;IPV4协议中&#xff0c;IP地址数量不充足的问题 NAT技术当前解决IP地址不够用的主要手段&#xff0c;是路由器的一个重要功能 NAT&#xff08;网络地址转换&#xff0c;Network Address Translation&#xff09;是一种…

新手村:逻辑回归-理解02:逻辑回归中的伯努利分布

新手村&#xff1a;逻辑回归-理解02&#xff1a;逻辑回归中的伯努利分布 伯努利分布在逻辑回归中的潜在含义及其与后续推导的因果关系 1. 伯努利分布作为逻辑回归的理论基础 ⭐️ 逻辑回归的核心目标是: 建模二分类问题中 目标变量 y y y 的概率分布。 伯努利分布&#xff08…

golang Error的一些坑

golang Error的一些坑 golang error的设计可能是被人吐槽最多的golang设计了。 最经典的err!nil只影响代码风格设计&#xff0c;而有一些坑会导致我们的程序发生一些与我们预期不符的问题&#xff0c;开发过程中需要注意。 ​​ errors.Is​判断error是否Wrap不符合预期 ​…

【STM32】知识点介绍二:GPIO引脚介绍

文章目录 一、概述二、GPIO的工作模式三、寄存器编程 一、概述 GPIO&#xff08;英语&#xff1a;General-purpose input/output&#xff09;,即通用I/O(输入/输出)端口&#xff0c;是STM32可控制的引脚。STM32芯片的GPIO引脚与外部设备连接起来&#xff0c;可实现与外部通讯、…

【AI】NLP

不定期更新&#xff0c;建议关注收藏点赞。 目录 transformer大语言模型Google Gemma疫情网民情绪识别 整体框架 baseline构建 模型调参、模型优化、其他模型 数据trick、指标优化、magic feature 数据增强、伪标签、迁移学习 模型融合sklearn中TFIDF参数详解 频率阈值可以去掉…