Vue3异步请求的最佳实践:async/await 与 Promise.then(),Vue3请求接口数据,以及TypeScript中的泛型和任意类型(Vue3请求API,TS中的T 和 any)

前言

在 Vue3 开发中,异步请求是非常常见的操作,特别是在与后端 API 交互时。虽然 Vue.js 自带了一些对异步数据的处理方式,但合理使用异步操作是确保应用顺畅运行的关键。这里我分享一下 Vue3 异步请求的最佳实践,并探讨 async/await 和 Promise.then() 两种处理异步操作的方式,简单记录一下

1. 请求接口的实现

这里,我们使用一个请求接口 callAskRecordList,它会从 /gpt/catalog 获取数据。这个接口封装了一个基于 get 的异步请求,同时为请求头添加了 Authorization 令牌,确保用户身份验证。

import { useUserStore } from '@/store' // 引入用户存储,用于获取用户信息
import { get } from "@/utils/request/get"; // 引入封装好的 GET 请求方法

// 查询参数方法,用泛型  以支持灵活的数据类型
export function callAskRecordList() {
  const userStore = useUserStore(); // 获取用户存储中的数据
  return get({
    // 设置请求的 URL 地址
    url: '/gpt/catalog', 
    // 设置请求头,包含 Bearer 令牌进行身份验证
    headers: { Authorization: `Bearer ` + userStore.userInfo.token }, 
  });
}




// 去掉注释
import { useAuthStore, useSettingStore, useUserStore } from '@/store'
import { get } from "@/utils/request/get";
export function callAskRecordList() {
  const userStore = useUserStore();
  return get({
    url: '/gpt/catalog',
    headers: { Authorization: `Bearer ` + userStore.userInfo.token },
  })
}


2. 在 Vue 组件中使用

接下来我们在 Vue 组件中直接使用这个接口。我们在 onMounted 生命周期钩子中触发异步请求,获取数据并更新组件的状态。在这个例子中,我们定义了一个 leftList,用于存储从后端获取的数据。

// 引入 Vue 中的 ref 进行响应式数据声明,引入 onMounted 钩子
import { ref, onMounted } from "vue"; 
// 引入我们定义的接口方法
import { callAskRecordList } from "@/api/userAsk.js"; 

// 定义一个响应式数据 leftList,用来存储从接口获取的数据
const leftList = ref([]);

// 定义异步函数,用于调用接口并更新数据
async function callSideList() {
  try {
    // 使用 await 等待异步请求结果,确保 res 是 Promise 的结果
    const res = await callAskRecordList();
    if (res && res.data) { // 判断返回结果是否存在,以及是否包含数据
      leftList.value = res.data; // 如果数据存在,将其赋值给 leftList
    }
  } catch (error) {
    // 如果请求过程中出现错误,捕获并输出到控制台
    console.error('Error fetching sider list:', error);
  }
}

// 页面挂载时触发,调用上面定义的 callSideList 函数获取数据
onMounted(() => {
  callSideList(); // 页面加载时调用异步函数
});




// 去掉注释
import { computed, ref, watch, onMounted } from "vue";
import { callAskRecordList1 } from "@/api/userAsk.js";
const leftList = ref([]);
async function callSideList() {
  try {
    const res = await callAskRecordList();
    if (res && res.data) {
      leftList.value = res.data;
    }
  } catch (error) {
    console.error('Error fetching sider list:', error);
  }
}
onMounted(() => {
  callSideList();
})


3. 为什么不建议在 async 函数中使用 .then()

有些同学习惯于在 async 函数中继续使用 .then() 语法,但这种做法可能会带来一些隐蔽的问题。比如,下面这种写法(不建议这样写):

async function callSideList() {
  try {
    // 使用 await 等待 Promise,但由于 .then() 没有返回,结果始终是 undefined
    await callAskRecordList().then(res => { 
      if (res && res.data) { // 判断返回的 res 是否存在及其数据
        leftList.value = res.data; // 将获取到的数据赋值给 leftList
      }
    });
  } catch (error) {
    console.error('Error fetching sider list:', error); // 捕获错误
  }
}




// 去掉注释
async function callSideList() {
  try {
    await callAskRecordList().then(res => {
      if (res && res.data) {
        leftList.value = res.data;
      }
    });
  } catch (error) {
    console.error('Error fetching sider list:', error);
  }
}

这样写的主要问题是 await 等待的实际上是 .then() 的返回值,而 .then() 没有显式返回一个值,默认返回 undefined。这种情况下,await 后的表达式始终是 undefined,容易导致代码逻辑出错。

更好的方式是(正确的做法是)直接使用 await

async function callSideList() {
  try {
    // 使用 await 直接等待 callAskRecordList 的结果,确保逻辑清晰
    const res = await callAskRecordList();
    if (res && res.data) { // 判断返回的 res 是否包含数据
      leftList.value = res.data; // 将数据赋值给 leftList
    }
  } catch (error) {
    console.error('Error fetching sider list:', error); // 捕获错误
  }
}




// 去掉注释
async function callSideList() {
  try {
    const res = await callAskRecordList();
    if (res && res.data) {
      leftList.value = res.data;
    }
  } catch (error) {
    console.error('Error fetching sider list:', error);
  }
}

这样做的好处是:

  • 代码更加简洁直观。
  • 确保 await 确实等待的是请求的返回结果。
  • 避免由于 .then() 返回 undefined 导致的潜在错误。


4. 使用传统的 .then().catch() 链式调用

当然,如果你不想使用 async/await,而是选择使用传统的链式调用处理异步操作,也可以像下面这样写:

function callSideList() {
  callAskRecordList()
    .then((res) => { // 成功请求后处理结果
      if (res && res.data) { // 判断 res 和其中的数据是否存在
        leftList.value = res.data; // 将获取到的数据赋值给 leftList
      }
    })
    .catch((error) => { // 捕获并处理请求错误
      console.error('Error fetching sider list:', error);
    });
}




// 去掉注释
function callSideList() {
  callAskRecordList()
    .then((res) => {
      if (res && res.data) {
        leftList.value = res.data;
      }
    })
    .catch((error) => {
      console.error('Error fetching sider list:', error);
    });
}

这种写法同样是可行的,只不过代码的可读性可能不如 async/await 版本好,尤其是在处理复杂异步逻辑时,.then() 链式调用可能会导致所谓的“回调地狱”,使得代码结构变得混乱。


5. 总结

        在 Vue.JS 中处理异步请求时,async/awaitPromise.then() 各有优劣。对于简单的异步操作,链式调用.then().catch()可以很好地解决问题,但在更复杂的异步场景下,async/await 提供了更加直观、简洁和易于维护的写法。

        此外,在使用 async/await 时,确保不要在 await 后面再接 .then(),避免在 async 函数中同时使用 .then()await,这样会导致返回 undefined,可能带来不可预见的错误。遵循这些最佳实践,将有助于提升代码的可读性和健壮性,也避免了一些不必要的潜在错误。


6. 泛型 T 和 任意类型 any

上文中有使用到泛型 T ,这里来介绍一下泛型 T 和任意类型 any 的使用,请看

1. T(泛型): 泛型是类型参数,用于创建可重用的函数、接口或类。在定义时并不指定具体类型,而是由调用者传入的实际类型来确定。泛型的目的是使代码更加灵活和可复用,同时保持类型安全。

例如:
function identity(arg: T): T {
  return arg;
}

let num = identity(5);  // T 被推断为 number
let str = identity("hello");  // T 被推断为 string

在这个例子中,identity 函数可以接受任意类型的参数,但在使用时类型是明确的,并且返回值会保留参数的类型。其中,(arg: T): T 语法含义

  • :泛型声明
  • (arg: T):参数类型
  • : T:返回类型

2. any any 是 TypeScript 中的一种类型,它表示任意类型。使用 any 时,TypeScript 不会对该变量进行类型检查,任何类型的值都可以赋给它,也可以从它那里获取任意类型的值。

例如:
function identityAny(arg: any): any {
  return arg;
}

let num = identityAny(5);  // 返回值类型是 any
let str = identityAny("hello");  // 返回值类型也是 any

这里,identityAny 函数可以接受任意类型的参数,但返回值类型也不确定,TypeScript 不会对此进行类型检查。

创作不易,感觉有用,就一键三连,感谢(●'◡'●)

你可能感兴趣的:(VueJS,前端,vue.js)