当前位置: 首页 > article >正文

异步处理之async/await使用技巧分享

前言

async/await是非常强大的语法糖,是处理异步问题的一种简洁、高效的方式。虽然它并非“最终解决方案”,但与Promise配合使用,确实能极大地简化异步编程的复杂性。

从字面上理解,async 表示一个函数是异步的,await 则用来等待一个异步操作完成。二者结合使用,使得代码看起来更加同步化,从而提高了可读性和维护性。

MDN文档
  • MDN: async/await

优点

  1. 提高代码可读性
    使用async/await,代码写起来类似于同步代码,从而避免了then链式调用中常见的回调地狱。

    async function() {
        const data1 = await getApiData(1);
        const data2 = await getApiData(2);
        const data3 = await getApiData(3);
    }
    
  2. 支持条件判断与流程控制
    可以直接在if条件语句中使用await,使得复杂逻辑更加简洁。

    if(await getApiData(true)){
      // ...
    }
    
  3. 提高复杂流程的清晰度
    使用async/await重构复杂的Promise链,代码会变得更加清晰易懂。

    原始Promise写法

    let arr = [];
    
    new Promise((resolve) => {
      if (arr.length) {
        resolve();
      } else {
        getApiData().then((res) => {
          if (res.data.length) {
            arr = res.data;
            resolve();
          } else {
            console.log('数据为空')
          }
        })
      }
    }).then(() => {
      arr.forEach(() => {});
    })
    

    重构为async/await写法

    let arr = [];
    
    async function fn() {
      if (!arr.length) {
        const res = await getApiData();
        if (res.data.length) {
          arr = res.data;
        } else {
          console.log('数据为空')
          return;
        }
      }
      arr.forEach(() => {})
    }
    fn();
    

缺点

  1. 错误处理麻烦
    异常需要使用try...catch进行捕获,如果请求接口出现错误,错误信息就需要在catch中处理。如果错误信息没有明确的区分,可能会影响代码的可维护性。
  2. 不能并行执行多个异步操作
    await会阻塞后续代码的执行,直到当前Promise完成。如果需要并行执行多个异步操作,需要额外的处理。
  3. 全局错误捕获问题
    异步函数中的全局错误需要通过window.onerror来捕获,但window.onerror会捕获一些不相关的错误,增加了系统开销。unhandledrejection事件可以用于捕获Promise中的未处理异常。
  4. 局部错误捕获问题
    try...catch语句内部的变量,无法被外部catch捕获,导致错误捕获范围有限。
  5. 不支持部分Promise方法
    async/await不支持像Promise.race()Promise.all()等原生Promise方法,因此如果你需要这些方法,建议直接使用Promise
  6. 调试问题
    在调试过程中,异步代码的行为可能不如预期,尤其在嵌套较深的async/await时,可能需要多次调试才能找到问题所在。

使用技巧

技巧1: 全局作用域下使用async/await

async函数只能在异步环境中调用,在全局作用域下不能直接使用async。你可以使用自执行匿名函数(IIFE)来解决这个问题:

(async () => {
  const data = await function();
})();
在React中使用

React的useEffect中不能直接使用async函数,因为它返回的是一个Promise,React期望useEffect返回一个清理函数。正确的做法是将async函数封装在一个内部函数中并执行:

useEffect(() => {
  async function fetchData() {
    const response = await MyAPI.getData(someId);
    // 处理数据
  }
  fetchData();
}, [someId]); // 或者[],如果依赖为空
技巧2: 在类方法中使用async/await

你可以在类方法中使用async/await来处理异步操作:

class Animal {
  async getAnimalInfo() {
    const res = await fetch('https://api.example.com/');
    return await res.json();
  }
}

const animal = new Animal();
(async () => {
  const data = await animal.getAnimalInfo();
})();
技巧3: 使用Promise.all来并行处理多个await

当你需要并行执行多个异步操作时,可以使用Promise.all()来同时发起多个异步请求:

const func = async () => {
  // 使用Promise.all来并行处理多个异步请求
  const [data1, data2] = await Promise.all([
    getData(1),
    getData(2)
  ]);
}
技巧4: 串行与并行的结合使用

你可以根据需求选择是否让异步操作串行或并行执行。

const func = async () => {
  // 串行
  const data1 = await getApiData(1);
  const data2 = await getApiData(2);

  // 并行
  const data3Promise = getApiData(3);
  const data4Promise = getApiData(4);
  const data5 = await data3Promise;
  const data6 = await data4Promise;
}
技巧5: 在循环中使用await

forEach中无法直接使用async/await,因此需要使用for...of循环或mapPromise.all来处理。

  • 串行
const func = async () => {
  const names = ['a', 'b'];
  for (const name of names) {
    const data = await getApiData(name);
  }
}
  • 并行
const func = async () => {
  const names = ['a', 'b'];
  const promises = names.map(x => getApiData(x));
  for (const name of promises) {
    const data = await name; // 等待Promise执行完成
  }
}
技巧6:使用async/await优化递归

递归函数是编程中的一种常用技术,async/await可以很容易地使递归函数进行异步操作。

/ 异步递归函数
async function func(nodes) {
    for (const node of nodes) {
        await asyncProcess(node);
        if (node.children) {
            await func(node.children);
        }
    }
}

// 示例
async function asyncProcess(node) {
    // 对节点进行异步处理逻辑
}
技巧7:结合async/await和事件循环

使用async/await可以更好地控制事件循环,像处理DOM事件或定时器等场合。

// 异步定时器函数
async function funcTimeout(fn, ms) {
    await new Promise(resolve => setTimeout(resolve, ms));
    fn();
}

// 示例
funcTimeout(() => console.log('Timeout after 2 seconds'), 2000);

总结

虽然async/await有一些缺点,比如无法直接处理Promiseraceall等原生方法,或者调试时的不便,但它仍然是现代JavaScript中非常强大的工具。通过合理的使用技巧,可以极大地提升代码的可读性与可维护性。

如果要处理更复杂的异步流,async/await结合Promise仍然是最推荐的方案。对于一些极其复杂的异步操作流,RxJS等工具是一个备选,但由于学习和维护成本较高,通常不推荐在简单项目中使用。

概览 | RxJS 中文文档


http://www.kler.cn/a/395208.html

相关文章:

  • mysql每日一题(上升的温度,date数据的计算)
  • 蓝桥杯竞赛单片机组备赛【经验帖】
  • vite + vue3 + ts解决别名引用@/api/user报错找不到相应的模块
  • Android 项目依赖库无法找到的解决方案
  • python习题练习
  • 整理iPhone空间:iphone怎么删除相簿
  • 【广西-柳州】《柳州市本级信息化建设项目预算支出标准(试行)》(柳财审〔2020〕16号 )-省市费用标准解读系列11
  • Windows搭建流媒体服务并使用ffmpeg推流播放rtsp和rtmp流
  • 【redis】redis
  • c# 在10万条数据中判断是否存在很慢问题
  • 【金猿案例展】科技日报——大数据科技资讯服务平台
  • DB-GPT系列(五):DB-GPT六大基础应用场景part2
  • pyinstaller+upx给python GUI程序添加自定义图标
  • 驾校增加无人机培训项目可行性技术分析
  • 本地搭建你的私有网盘:在Ubuntu上使用Portainer CE安装NextCloud
  • 基于springboot+vue实现的高校电子图书馆的大数据平台 (源码+L文+ppt)4-013
  • Jmeter中的配置原件(四)
  • 机器学习周报(transformer学习1)
  • PG数据库 数据库时间字段 开始时间和结束时间,判断和查询条件的开始和截止时间存在交集,SQL如何编写
  • vue请求数据报错,设置支持跨域请求,以及2种请求方法axios或者async与await
  • golang反射函数注册
  • (十六)JavaWeb后端开发——Spring框架常见注解
  • 【C++】C++基础知识
  • 翼鸥教育:从OceanBase V3.1.4 到 V4.2.1,8套核心集群升级实践
  • 使用 Python 向 IP 地址发送字符串 —— TCP 与 UDP 协议详解
  • 蓝桥杯——数组