【TypeScript】异步编程

embedded/2024/10/4 15:04:09/

文章目录

  • 异步编程
    • 1. TypeScript 中的异步编程
      • 1.1 基本用法
        • 简单的异步函数
        • 异步操作与延迟
        • 并行执行多个异步操作
        • 处理错误
        • 使用 Promise.all 处理多个异步操作的错误
    • 2. 在 TypeScript 中使用 Vite 和 FastAPI
      • 2.1 安装 Node.js
      • 2.2 创建 Vue 3 + TypeScript 项目
      • 2.3 安装依赖
      • 2.4 启动开发服务器
      • 2.5 单文件组件
        • HelloWorld.vue 示例
      • 2.6 FastAPI 后端
        • 安装 FastAPI
        • 交互式 API 文档
      • 2.7 发起 POST 请求
        • 修改后的 HelloWorld.vue
        • 更新后的 FastAPI 后端
      • 2.8 测试
    • 3. 封装 Axios 的必要性与实现
      • 3.1 为什么要封装 Axios
      • 3.2 Axios 类型声明
        • 封装 Axios 实例
      • 3.3 创建 API 管理文件
        • type.ts 示例
        • api.ts 示例
      • 3.5 在组件中使用 API
        • 使用方式一:顶层 `await`
        • 使用方式二:函数内 `async/await`
    • 学习总结

异步编程

1. TypeScript 中的异步编程

在 TypeScript 中,异步编程通常通过 asyncawait 关键字来实现,这使得处理异步操作更加优雅和直观。这些特性是基于 ES2017(ES8)标准的,允许我们编写可读性更强的异步代码。

1.1 基本用法

简单的异步函数

首先,我们定义一个简单的异步函数,它返回一个 Promise 对象:

typescript">async function greet(): Promise<string> {return Promise.resolve("Hello, async world!");
}async function execute() {const message = await greet();console.log(message); // 输出: Hello, async world!
}execute();

在这个例子中,greet 函数是异步的,返回一个 Promiseexecute 函数使用 await 来获取 greet 函数的结果。

异步操作与延迟

我们可以模拟一个延迟操作,例如从服务器获取数据:

typescript">async function fetchData(): Promise<number> {return new Promise((resolve) => {setTimeout(() => resolve(42), 500); // 500 毫秒后返回 42});
}async function run() {const data = await fetchData();console.log(data); // 输出: 42
}run();

这里的 fetchData 函数在 500 毫秒后解决,并返回一个数字。

并行执行多个异步操作

我们可以并行执行多个异步函数,使用 Promise.all

typescript">async function fetchFirstValue(): Promise<number> {return new Promise((resolve) => {setTimeout(() => resolve(10), 1000);});
}async function fetchSecondValue(): Promise<number> {return new Promise((resolve) => {setTimeout(() => resolve(20), 1000);});
}async function execute() {const [first, second] = await Promise.all([fetchFirstValue(), fetchSecondValue()]);console.log(first + second); // 输出: 30
}execute();

在这个示例中,两个异步操作同时执行,最后将它们的结果相加。

处理错误

可以通过 try...catch 块来捕获异步函数中的错误:

typescript">async function faultyOperation(): Promise<number> {throw new Error("An error occurred");
}async function run() {try {const result = await faultyOperation();} catch (error) {console.error(error.message); // 输出: An error occurred}
}run();

这里,faultyOperation 函数抛出一个错误,我们在 run 函数中捕获并处理它。

使用 Promise.all 处理多个异步操作的错误

如果多个异步操作中有一个失败,可以使用 Promise.all 来处理:

typescript">async function failOperation(): Promise<number> {return new Promise((_, reject) => {setTimeout(() => reject('Operation failed'), 300);});
}async function successOperation(): Promise<number> {return new Promise((resolve) => {setTimeout(() => resolve(25), 300);});
}async function run() {try {const [result1, result2] = await Promise.all([failOperation(), successOperation()]);console.log(result1 + result2);} catch (error) {console.error(error); // 输出: Operation failed}
}run();

在这个示例中,failOperation 将会拒绝,而 Promise.all 会立即捕获这个拒绝,并在 run 函数中处理。

2. 在 TypeScript 中使用 Vite 和 FastAPI

2.1 安装 Node.js

确保你已经安装了 Node.js(推荐使用最新的稳定版)。然后在命令行中运行以下命令来全局安装 Vite:

npm install -g create-vite

2.2 创建 Vue 3 + TypeScript 项目

使用 Vite 创建一个新的 Vue 3 + TypeScript 项目,运行以下命令并替换 vue3ts 为你的项目名称:

create-vite vue3ts --template vue-ts

2.3 安装依赖

进入项目目录并安装依赖:

cd vue3ts
npm install

2.4 启动开发服务器

启动开发服务器:

npm run dev

在浏览器中输入 http://localhost:5173/,如果能看到页面,则说明环境搭建成功。

2.5 单文件组件

打开文件 src/components/HelloWorld.vue,我们将开始修改这个单文件组件。

HelloWorld.vue 示例
<template><button @click="get_query()">发起GET请求</button>
</template><script setup lang='ts'>javascript">
import axios from 'axios';interface TestData {message: string;
}const get_query = () => {// 发起 GET 请求axios.get<TestData>('http://127.0.0.1:8009/').then(response => {const testData: TestData = response.data;console.log(testData.message);}).catch(error => {console.error(error);});
}
</script>

2.6 FastAPI 后端

确保你使用 Python 3.8 以上版本(推荐 Python 3.10.5)。检查 Python 版本:

import sys
print(sys.version)
安装 FastAPI

安装 FastAPI:

pip install fastapi -i https://pypi.tuna.tsinghua.edu.cn/simple

FastAPI 会自动安装 Uvicorn。创建一个 test.py 文件并粘贴以下代码:

import uvicorn
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddlewareapp = FastAPI()
app.add_middleware(CORSMiddleware,allow_origins=["*"],allow_credentials=False,allow_methods=["*"],allow_headers=["*"]
)@app.get("/")
async def root():return {"message": "Hello World"}if __name__ == '__main__':uvicorn.run(app, host='0.0.0.0', port=8009)

test.py 所在目录打开终端,运行:

python test.py

访问地址为 http://127.0.0.1:8009/,你将看到如下 JSON 响应:

{"message": "Hello World"}
交互式 API 文档

在浏览器中输入 http://127.0.0.1:8009/docs,查看自动生成的交互式 API 文档。

2.7 发起 POST 请求

修改 HelloWorld.vue,实现 POST 请求的功能。

修改后的 HelloWorld.vue
<template><button @click="get_query()">发起POST请求</button>
</template><script setup lang='ts'>javascript">
import axios from 'axios';interface TestData {item_id: string;name: string;
}const get_query = () => {// 发起 POST 请求axios.post<TestData>('http://127.0.0.1:8009/items/', { item_id: "5", name: "hello" }).then(response => {const testData: TestData = response.data;console.log(testData.item_id);console.log(testData.name);}).catch(error => {console.error(error);});
}
</script>
更新后的 FastAPI 后端
from fastapi import Body, FastAPI
from fastapi.middleware.cors import CORSMiddleware
from typing import Annotatedapp = FastAPI()
app.add_middleware(CORSMiddleware,allow_origins=["*"],allow_credentials=False,allow_methods=["*"],allow_headers=["*"]
)@app.post("/items/")
async def read_item(item_id: Annotated[int, Body()], name: Annotated[str, Body()]):return {"item_id": item_id, "name": name}

2.8 测试

按 F12 打开浏览器控制台,点击按钮后,控制台将看到输出:

5
hello

这样,你的前端与后端交互已成功实现。

3. 封装 Axios 的必要性与实现

3.1 为什么要封装 Axios

封装 Axios 主要是为了利用 TypeScript 的优势,确保类型检查和语法提示,从而提高代码的安全性和便捷性。具体而言,封装的好处包括:

  1. 统一处理请求头:通过创建 Axios 实例,可以集中管理请求头,避免每次请求都重复设置。

  2. 接口统一管理:将所有接口集中管理,使代码更清晰,易于维护,避免重复的代码。

  3. 避免回调地狱:使用 async/await 语法,提高代码的可读性,避免嵌套的 .then() 结构。

3.2 Axios 类型声明

Axios 提供了完备的类型声明,允许我们在 TypeScript 中准确地定义请求和响应的数据类型。核心方法如 requestgetpost 都支持泛型,从而使得我们可以指定响应数据的类型。

封装 Axios 实例

src 文件夹下创建一个 request 文件夹,并添加 base.ts 文件:

typescript">import axios from 'axios';// 创建 axios 实例
const instance = axios.create({baseURL: '', // 请求地址前缀timeout: 80000, // 请求超时时间withCredentials: true, // 异步请求携带 cookie
});// 请求拦截器
instance.interceptors.request.use(config => {// 可在此添加 tokenreturn config;},error => Promise.reject(error)
);// 响应拦截器
instance.interceptors.response.use(response => response.data,error => {if (error.response.status === 401) {localStorage.removeItem("x-auth-token");// 可在此跳转登录页}return Promise.reject(error);}
);export default instance;

3.3 创建 API 管理文件

request 文件夹下,为不同模块创建文件夹,比如 user,并添加 type.tsapi.ts 文件。

type.ts 示例
typescript">export interface ReqLogin {username: string;password: string;
}export interface ProData {projects?: string;detail?: { code: number; message: string; data: string };
}export interface ItypeAPI {id: number;username: string;email: string;token: string;
}
api.ts 示例
typescript">import instance from "../base";
import { ReqLogin, ProData, ItypeAPI } from "./type";const headers = { "content-type": "application/x-www-form-urlencoded" };// 登录请求
export const loginAPI = (data: ReqLogin): Promise<ItypeAPI> =>instance.post("/v1/users/token", data, { headers });export const checkAPI = (): Promise<ProData> =>instance.get("/v1/users/pro", { headers });

3.5 在组件中使用 API

使用方式一:顶层 await
<script setup lang="ts">javascript">
import { checkAPI } from "../../request/api";let res = await checkAPI();
console.log(res);
</script>
使用方式二:函数内 async/await
<script setup lang="ts">javascript">
import { loginAPI } from '../request/user/api';const data = { username: "mockuser", password: "123456" };const get_query = async () => {let res = await loginAPI(data);console.log(res);
};
</script>

通过这样的封装,可以有效地提升代码的可读性和维护性,同时利用 TypeScript 提供的强类型检查,确保数据的安全性。

学习总结

使用 asyncawait,我们可以以一种更简洁和可读的方式编写异步代码。通过捕获错误和并行处理多个操作,我们可以确保代码的稳定性和可靠性。
最后一章居然讲解了axiso的源码,我哭死。本人亦因此开启了阅读前端框架源码的习惯。通过封装axiso,可以有效地提升代码的可读性和维护性,同时利用 TypeScript 提供的强类型检查,确保数据的安全性。


http://www.ppmy.cn/embedded/121292.html

相关文章

【尚硅谷】RocketMQ 消息队列学习笔记

RocketMQ 和 Kafka 消息队列概念比较&#xff1f; 好的&#xff01;RocketMQ 和 Kafka 都是分布式消息队列系统&#xff0c;它们的核心概念有很多相似之处&#xff0c;但在具体实现和命名上有所不同。下面我通过一个表格来对比 RocketMQ 和 Kafka 中的五个概念&#xff1a;消息…

【rCore OS 开源操作系统】Rust mod模块和static生命周期 知识点及练习题

【rCore OS 开源操作系统】Rust 模块 mod 知识点及练习题 前言 虽然是模块的章节&#xff0c;但是也涉及到了一些结构体访问控制、生命周期 相关的知识点。 这里暂不讨论 crate&#xff0c;个人觉得这个东西属于工程化的内容&#xff0c;而不是 rust 的模块语法。 知识点 mo…

激光切割机适用材质有哪些

激光切割机是一种利用激光束对各种材料进行高精度、高速度切割的机器设备。其适用材质广泛&#xff0c;包括但不限于以下两大类&#xff1a; 一、金属材料 不锈钢&#xff1a;激光切割机较容易切割不锈钢薄板&#xff0c;使用高功率YAG激光切割系统&#xff0c;切割不锈钢板的…

SQL:如果字段需要排除某个值但又有空值时,不能直接用“<>”或not in

在 SQL 中&#xff0c;如果字段需要排除某个值但又有空值存在时&#xff0c;不能直接使用“<>”&#xff08;不等于&#xff09;或 NOT IN&#xff0c;是因为这些操作会把空值也考虑进去&#xff0c;但通常情况下可能并不希望空值被这样处理。 以下是一些解决方法&#…

RabbitMQ的高级特性-限流

消息分发: RabbitMQ队列拥有多个消费者时, 队列会把收到的消息分派给不同的消费者. 每条消息只会发送给订阅列表⾥的⼀个消费者. 这种⽅式⾮常适合扩展, 如果现在负载加重,那么只需要创建更多的消费者来消费处理消息即可. 默认情况下, RabbitMQ是以轮询的⽅法进⾏分发的, ⽽不管…

GPT理论

1.GPT发展 Transformer是一个用作翻译任务的模型&#xff0c;谷歌出品。 GPT全称 lmproving Language Understanding by Generative Pre-Training&#xff0c;用预训练语言理解模型。OPENAI出品。 BERT全称Pre-training of Deep BidirectionalTransformers for Language Unde…

CSS多列

CSS多列 前言 有的时候希望文本能按照多列效果显示&#xff0c;如&#xff1a; 这时候就要把文本显示效果改成多列显示&#xff0c;标题独占一行 CSS文本多列使用 ① column-count 指定文本分为几列&#xff0c;如&#xff1a; column-count: 3;② column-gap 指定列之…

如何实现一个Mini Spring Boot

Spring Boot 是一个强大的框架&#xff0c;简化了 Spring 应用程序的开发。但是&#xff0c;它的核心思想和实现其实并不复杂。接下来&#xff0c;我们将从零开始&#xff0c;逐步实现一个简化版的 “Mini Spring Boot”。 1. 核心思想 Spring Boot 的核心功能包括&#xff…