【TypeScript】异步编程

文章目录

  • 异步编程
    • [1. TypeScript 中的异步编程](#1. TypeScript 中的异步编程)
    • [2. 在 TypeScript 中使用 Vite 和 FastAPI](#2. 在 TypeScript 中使用 Vite 和 FastAPI)
      • [2.1 安装 Node.js](#2.1 安装 Node.js)
      • [2.2 创建 Vue 3 + TypeScript 项目](#2.2 创建 Vue 3 + TypeScript 项目)
      • [2.3 安装依赖](#2.3 安装依赖)
      • [2.4 启动开发服务器](#2.4 启动开发服务器)
      • [2.5 单文件组件](#2.5 单文件组件)
        • [HelloWorld.vue 示例](#HelloWorld.vue 示例)
      • [2.6 FastAPI 后端](#2.6 FastAPI 后端)
        • [安装 FastAPI](#安装 FastAPI)
        • [交互式 API 文档](#交互式 API 文档)
      • [2.7 发起 POST 请求](#2.7 发起 POST 请求)
        • [修改后的 HelloWorld.vue](#修改后的 HelloWorld.vue)
        • [更新后的 FastAPI 后端](#更新后的 FastAPI 后端)
      • [2.8 测试](#2.8 测试)
    • [3. 封装 Axios 的必要性与实现](#3. 封装 Axios 的必要性与实现)
      • [3.1 为什么要封装 Axios](#3.1 为什么要封装 Axios)
      • [3.2 Axios 类型声明](#3.2 Axios 类型声明)
        • [封装 Axios 实例](#封装 Axios 实例)
      • [3.3 创建 API 管理文件](#3.3 创建 API 管理文件)
        • [type.ts 示例](#type.ts 示例)
        • [api.ts 示例](#api.ts 示例)
      • [3.5 在组件中使用 API](#3.5 在组件中使用 API)
        • [使用方式一:顶层 `await`](#使用方式一:顶层 await)
        • [使用方式二:函数内 `async/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:

bash 复制代码
npm install -g create-vite

2.2 创建 Vue 3 + TypeScript 项目

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

bash 复制代码
create-vite vue3ts --template vue-ts

2.3 安装依赖

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

bash 复制代码
cd vue3ts
npm install

2.4 启动开发服务器

启动开发服务器:

bash 复制代码
npm run dev

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

2.5 单文件组件

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

HelloWorld.vue 示例
html 复制代码
<template>
    <button @click="get_query()">发起GET请求</button>
</template>

<script setup lang='ts'>
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 版本:

python 复制代码
import sys
print(sys.version)
安装 FastAPI

安装 FastAPI:

bash 复制代码
pip install fastapi -i https://pypi.tuna.tsinghua.edu.cn/simple

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

python 复制代码
import uvicorn
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware

app = 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 所在目录打开终端,运行:

bash 复制代码
python test.py

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

json 复制代码
{"message": "Hello World"}
交互式 API 文档

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

2.7 发起 POST 请求

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

修改后的 HelloWorld.vue
html 复制代码
<template>
    <button @click="get_query()">发起POST请求</button>
</template>

<script setup lang='ts'>
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 后端
python 复制代码
from fastapi import Body, FastAPI
from fastapi.middleware.cors import CORSMiddleware
from typing import Annotated

app = 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 => {
        // 可在此添加 token
        return 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
html 复制代码
<script setup lang="ts">
import { checkAPI } from "../../request/api";

let res = await checkAPI();
console.log(res);
</script>
使用方式二:函数内 async/await
html 复制代码
<script setup lang="ts">
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 提供的强类型检查,确保数据的安全性。

相关推荐
开心工作室_kaic11 分钟前
基于微信小程序的校园失物招领系统的设计与实现(论文+源码)_kaic
c语言·javascript·数据库·vue.js·c#·旅游·actionscript
Small-K11 分钟前
前端框架中@路径别名原理和配置
前端·webpack·typescript·前端框架·vite
bin915315 分钟前
【EXCEL数据处理】000009 案列 EXCEL单元格数字格式。文本型数字格式和常规型数字格式的区别
大数据·前端·数据库·信息可视化·数据分析·excel·数据可视化
山语山34 分钟前
C语言——文件读写操作
java·c语言·前端·microsoft·visual studio
太阳火神的美丽人生1 小时前
Vant WeApp 开启 NPM 遇到的问题总结
前端·npm·node.js
lucifer3111 小时前
JavaScript 中的装饰器模式(十一)
javascript·设计模式
哈哈哈哈cwl1 小时前
一篇打通浏览器储存
前端·面试·浏览器
凌云行者2 小时前
使用rust写一个Web服务器——async-std版本
服务器·前端·rust
等什么君!2 小时前
JavaScript数据类型
开发语言·前端·javascript
DK七七2 小时前
【PHP陪玩系统源码】游戏陪玩系统app,陪玩小程序优势
前端·vue.js·游戏·小程序·php·uniapp