使用 TypeScript 改进异步操作错误处理策略

处理异步代码是 JavaScript 应用程序的主要内容。TypeScript 为异步操作带来类型安全,增强可预测性并减少运行错误本文旨在探索我们可以利用的模式来有效地管理异步操作错误处理

Async/Await

Async/await 语法使代码更清晰、更易读,与同步执行非常相似。TypeScript 的类型推断与此一致,确保在编译检查变量返回类型,从而减少可能的运行错误

async function fetchData(url: string): Promise<string&gt; {
  try {
    const response = await fetch(url);
    if (!response.ok) {
      throw new Error(`Error: ${response.statusText}`);
    }

    return await response.text();
  } catch (error: unknown) {
    // 保留堆栈跟踪
    throw error instanceof Error ? error : new Error("Unexpected error");
  }
}

Promise:确保异步操作中的类型安全

TypeScript 通过解析值和可能发生的任何错误强制执行类型来改进Promise 。这种编译类型检查会导致异步操作中的输出更加可预测,从而显着降低意外运行错误风险

const taskResult: Promise<string&gt; = new Promise((resolve, reject) =&gt; {
  const someCondition = true;
  if (someCondition) {
    resolve("Success!");
  } else {
    // TypeScript 确保这是一个 Error 对象
    reject(new Error("Failure"));
  }
});

示例演示了 TypeScript 确保检查错误对象类型的能力,从而实现细粒度和弹性错误处理

增强的泛型错误处理

TypeScript 中的泛型增强了函数灵活性,同时保持了类型安全考虑一个获取不同类型内容的异步函数。泛型允许该函数清楚地定义返回类型,确保编译时类型安全

enum ResponseKind {
  Article = "article",
  Comment = "comment",
  Error = "error",
}

type ArticleResponse = {
  kind: ResponseKind.Article;
  title: string;
  content: string;
};

type CommentResponse = {
  kind: ResponseKind.Comment;
  content: string;
};

type ErrorResponse = {
  kind: ResponseKind.Error;
  message: string;
};

// 使用可区分联合来定义响应类型
type ContentResponse = ArticleResponse | CommentResponse | ErrorResponse;

async function getContent<T extends ContentResponse&gt;(
  contentId: string
): Promise<Exclude<T, ErrorResponse&gt;&gt; {
  const response: ContentResponse = await fetchContent(contentId);
  if (response.kind === ResponseKind.Error) {
    throw new Error(response.message);
  }
  return response as Exclude<T, ErrorResponse&gt;;
}

// 将 getContent 函数与类型断言一起使用,
// 强化我们的预期返回类型,以实现更可预测行为和类型安全
async function displayContent(contentId: string) {
  try {
    // 这里我们断言响应的类型为 ArticleResponse
    const article = await getContent<ArticleResponse>(contentId);

    // 对“title属性的类型安全访问
    console.log(article.title);
  } catch (error) {
    console.error(error);
  }
}

上面的函数getContent说明如何使用泛型在编译实现类型安全,确保我们正确处理各种内容类型。这种方法显着降低了运行错误的可能性。

此外,我们利用Exclude来确保getContent不返回ErrorResponse,这是 TypeScript 的类型系统如何通过设计防止某些类运行时错误的示例

尽管 TypeScript 有强大的编译检查,但有些错误本质上是运行时的,需要显式处理接下来,我们将了解自定义错误处理如何充当那些无法通过编译检查的错误的请求

继续我们的类型安全数据获取实践,针对运行时错误制定稳健的策略至关重要。下面对自定义错误类的介绍提供了有效区分和处理此类错误的详细方法

class BadRequestError extends Error {
  public statusCode: number;

  constructor(message: string, statusCode = 400) {
    super(message);
    this.name = "BadRequestError";

    // 错误请求的默认 HTTP 400 状态代码
    this.statusCode = statusCode;
  }
}

type UserData = {
  name: string;
};
async function submitUserData(userData: UserData): Promise<void> {
  try {
  	// 数据提交
    validateUserData(userData);
  } catch (error) {
    // 处理错误请求错误
    if (error instanceof BadRequestError) {
      console.error(`Validation failed: ${error.message}`);
    // 处理意外错误
    } else {
      console.error(`Unexpected error: ${error.message}`);
    }

    // 如果想从更高层的调用函数访问错误,则重新抛出错误
    throw error;
  }
}

function validateUserData<T extends UserData>(data: T): void {
  if (!data.name) {
    throw new BadRequestError("Name is required");
  }
}

通过自定义错误类,我们可以以精细的方式处理异常,补充泛型提供的编译时类型安全性。通过结合这些策略,我们创建一个弹性系统,在编译时和运行时维护类型安全,为我们的 TypeScript 应用程序提供全面的错误处理。

结果类型的替代错误处理

在处理异步操作时,函数式编程风格可能特别有用。Result类型或者Either模式提供了传统错误处理的结构化替代方案。这种方法将错误视为数据,将它们封装可以通过异步流轻松传播结果类型中。

type Success<T> = { kind: 'success', value: T };
type Failure<E> = { kind: 'failure', error: E };
type Result<T, E = Error> = Success<T> | Failure<E>;
type AsyncResult<T, E = Error> = Promise<Result<T, E>>;

async function asyncComplexOperation(): AsyncResult<number> {
    try {
        // 异步逻辑
        const value = await someAsyncTask();
        return { kind: 'success', value };
    } catch (error) {
        return {
            kind: 'failure',
            error: error instanceof Error ? error : new Error('Unknown error'),
        };
    }
}

异步操作中的结构化错误处理

对于更复杂的异步应用程序,我们可能希望使用错误边界类型来处理更高级别的错误。此模式旨在与 async/await 语法很好地配合使用,允许干净且可预测捕获错误并处理上游。

type ErrorBoundary<T, E extends Error> = {
  status: 'success';
  data: T;
} | {
  status: 'error';
  error: E;
};

async function asyncHandleError<T>(
  fn: () => Promise<T>,
  createError: (message?: string) => Error
): Promise<ErrorBoundary<T, Error>> {
  try {
    const data = await fn();
    return { status: 'success', data };
  } catch (error) {
    const errorMessage = error instanceof Error ? error.message : 'Unknown error';
    return {
      status: 'error',
      error: createError(errorMessage)
    };
  }
}

async function riskyAsyncOperation(): Promise<string> {
  const someCondition = false;
  if (someCondition) {
      throw new Error('Failure');
  }
  return 'Success';
}

async function handleOperation() {
  const result = await asyncHandleError(riskyAsyncOperation, (message) => new Error(message));
  
  if (result.status === 'success') {
      console.log(result.data); 
  } else {
      console.error(result.error.message);
  }
}

handleOperation();

在ErrorBoundary模式的异步适应中,asyncHandleError函数采用异步函数并返回一个解析为成功或错误对象的Promise。这可确保以结构化和类型安全的方式处理异步错误,从而促进 TypeScript 代码更好的错误管理实践。

总结

本文提出了使用 TypeScript 改进异步操作和错误处理的策略,从而增强代码的健壮性和可维护性。我们通过实际示例说明了这些模式,强调了它们在现实场景中的适用性。

原文地址:https://blog.csdn.net/qq_42880714/article/details/134723437

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任

如若转载,请注明出处:http://www.7code.cn/show_15921.html

如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱:suwngjj01@126.com进行投诉反馈,一经查实,立即删除

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注