МенюКОМЭКСПО WORK
КОМЭКСПО
Возможности платформы

Курсы и специалистов можно выбирать без входа.

Ещё в КОМЭКСПО

Другие возможности

Нужна помощь?Напишите нам — поможем разобраться
КОМЭКСПО СкиллыОткрыть ассистента ↗
← Все навыки

fp async

Practical async patterns using TaskEither - clean pipelines instead of try/catch hell, with real API examples

Каталог из GitHub · что мы проверяем

Собираем навыки из Agentic Awesome Skills на GitHub. Это работы авторов сообщества, а не собственные разработки КОМЭКСПО. В карточках сохраняем источник и фиксированную версию.

Автоматические проверки

  • При импорте: проверяем адреса источников и убираем повторяющиеся идентификаторы. Некоторые категории риска исключаем.
  • При загрузке инструкции: проверяем формат SKILL.md, кодировку и объём. Ищем упоминания дополнительных файлов и инструментов.
  • Сверяем обозначение лицензии с разрешённым списком и учитываем известные исключения источника. Непонятные условия требуют отдельной проверки.

«Требует проверки» означает, что инструкция ещё не загружена и не проверена. Статусы совместимости не подтверждают безопасность, качество ответа или работу во всех моделях. Полный аудит кода, прав и тестирование каждого навыка не проводились. Скрипты не запускаем.

Авторы и условия использования

Оригинальные тексты коллекции заявлены под CC BY 4.0; у сторонних материалов могут быть другие условия. Открытый GitHub не означает отсутствие авторских прав. Сохраняйте авторство, ссылку на лицензию и отметки об изменениях.

Русские пояснения и промпт-обёртки подготовлены КОМЭКСПО. Оригинальные инструкции не переведены. Мы не связаны с GitHub или авторами навыков и не заявляем об их одобрении сервиса. Правообладателям: контакты — укажите карточку, оригинал и суть обращения.

Использовать навык

Без регистрации

Вставьте в свой ИИ-чат и замените последнюю строку своей задачей. Это инструкция, а не подключение новых инструментов.

Скачать SKILL.md ↓

Источник: sickn33/agentic-awesome-skills · CC-BY-4.0 · Оригинал ↗

Атрибуция для копирования и распространения

Если распространяете скачанный файл, приложите атрибуцию и требуемые лицензией уведомления автора. При изменении инструкции укажите свои изменения. Условия лицензии

Как это работает

Навык задаёт подход к задаче: например, как редактировать текст или проверять код. Вы выбираете его в чате — ИИ получает эту инструкцию вместе с вашим запросом.

Поддерживаются текст, код и создание сайтов. Навык не подключает новые модели, терминал, MCP или аккаунты. Для изображений и видео используются отдельные инструменты.

Совместимость
Нужны доп. инструменты
Версия
465ad05638fb · фиксируется при добавлении
Лицензия
CC-BY-4.0
Звёзды репозитория
47 140

Ограничения оригинала

  • В оригинале есть ссылки на файлы или внешние инструменты. Они не устанавливаются; в чате применяются только инструкции SKILL.md.
Оригинальная инструкция SKILL.md · 7 470 символов
---
name: fp-async
description: Practical async patterns using TaskEither - clean pipelines instead of try/catch hell, with real API examples
risk: critical
source: community
date_added: "2026-09-04"
version: 1.0.0
author: kadu
tags:
  - fp-ts
  - typescript
  - async
  - error-handling
  - practical
  - promises
  - api
  - fetch
---

# Practical Async Patterns with fp-ts

Stop writing nested try/catch blocks. Stop losing error context. Start building clean async pipelines that handle errors properly.

**TaskEither is simply an async operation that tracks success or failure.** That's it. No fancy terminology needed.

## Detailed Guide

Read [the detailed guide](references/detailed-guide.md) before executing this skill. It retains the complete procedure and reference material. Treat its safety, prerequisites, and validation requirements as mandatory. For focused work, load the relevant sections; for end-to-end work, read the guide completely.

## When to Use
- You need async error handling in TypeScript with `TaskEither`.
- The task involves wrapping Promises, composing API calls, or replacing nested `try/catch` flows.
- You want practical fp-ts async patterns instead of academic explanations.

```typescript
// TaskEither<Error, User> means:
// "An async operation that either fails with Error or succeeds with User"
```

---

## 5. Real API Examples

### Complete Fetch Wrapper

```typescript
// types.ts
interface ApiError {
  code: string
  message: string
  status: number
  details?: unknown
}

// api.ts
const createApiError = (
  code: string,
  message: string,
  status: number,
  details?: unknown
): ApiError => ({ code, message, status, details })

const request = <T>(
  url: string,
  options: RequestInit = {}
): TE.TaskEither<ApiError, T> =>
  TE.tryCatch(
    async () => {
      const response = await fetch(url, {
        headers: {
          'Content-Type': 'application/json',
          ...options.headers,
        },
        ...options,
      })

      if (!response.ok) {
        const body = await response.json().catch(() => ({}))
        throw createApiError(
          body.code || 'HTTP_ERROR',
          body.message || response.statusText,
          response.status,
          body
        )
      }

      // Handle 204 No Content
      if (response.status === 204) {
        return undefined as T
      }

      return response.json()
    },
    (error): ApiError => {
      if (typeof error === 'object' && error !== null && 'code' in error) {
        return error as ApiError
      }
      return createApiError(
        'NETWORK_ERROR',
        error instanceof Error ? error.message : 'Request failed',
        0
      )
    }
  )

// API client
const api = {
  get: <T>(url: string) => request<T>(url),

  post: <T>(url: string, body: unknown) =>
    request<T>(url, {
      method: 'POST',
      body: JSON.stringify(body)
    }),

  put: <T>(url: string, body: unknown) =>
    request<T>(url, {
      method: 'PUT',
      body: JSON.stringify(body)
    }),

  delete: (url: string) =>
    request<void>(url, { method: 'DELETE' }),
}

// Usage
const getUser = (id: string) => api.get<User>(`/api/users/${id}`)
const createUser = (data: CreateUserDto) => api.post<User>('/api/users', data)
const updateUser = (id: string, data: UpdateUserDto) => api.put<User>(`/api/users/${id}`, data)
const deleteUser = (id: string) => api.delete(`/api/users/${id}`)
```

### Database Operations (Prisma Example)

```typescript
import { PrismaClient, Prisma } from '@prisma/client'

type DbError =
  | { _tag: 'NotFound'; entity: string; id: string }
  | { _tag: 'UniqueViolation'; field: string }
  | { _tag: 'ConnectionError'; cause: unknown }

const prisma = new PrismaClient()

const wrapPrisma = <T>(
  operation: () => Promise<T>
): TE.TaskEither<DbError, T> =>
  TE.tryCatch(
    operation,
    (error): DbError => {
      if (error instanceof Prisma.PrismaClientKnownRequestError) {
        if (error.code === 'P2002') {
          const field = (error.meta?.target as string[])?.join(', ') || 'unknown'
          return { _tag: 'UniqueViolation', field }
        }
        if (error.code === 'P2025') {
          return { _tag: 'NotFound', entity: 'Record', id: 'unknown' }
        }
      }
      return { _tag: 'ConnectionError', cause: error }
    }
  )

// Repository pattern
const userRepository = {
  findById: (id: string): TE.TaskEither<DbError, User> =>
    pipe(
      wrapPrisma(() => prisma.user.findUnique({ where: { id } })),
      TE.chain(user =>
        user
          ? TE.right(user)
          : TE.left({ _tag: 'NotFound', entity: 'User', id })
      )
    ),

  findByEmail: (email: string): TE.TaskEither<DbError, User | null> =>
    wrapPrisma(() => prisma.user.findUnique({ where: { email } })),

  create: (data: CreateUserInput): TE.TaskEither<DbError, User> =>
    wrapPrisma(() => prisma.user.create({ data })),

  update: (id: string, data: UpdateUserInput): TE.TaskEither<DbError, User> =>
    wrapPrisma(() => prisma.user.update({ where: { id }, data })),

  delete: (id: string): TE.TaskEither<DbError, void> =>
    pipe(
      wrapPrisma(() => prisma.user.delete({ where: { id } })),
      TE.map(() => undefined)
    ),
}

// Service using repository
const createUserService = (input: CreateUserInput) =>
  pipe(
    // Check email doesn't exist
    userRepository.findByEmail(input.email),
    TE.chain(existing =>
      existing
        ? TE.left({ _tag: 'UniqueViolation' as const, field: 'email' })
        : TE.right(undefined)
    ),
    // Create user
    TE.chain(() => userRepository.create(input))
  )
```

### File Operations (Node.js)

```typescript
import * as fs from 'fs/promises'
import * as path from 'path'

type FileError =
  | { _tag: 'NotFound'; path: string }
  | { _tag: 'PermissionDenied'; path: string }
  | { _tag: 'IoError'; cause: unknown }

const toFileError = (error: unknown, filePath: string): FileError => {
  if (error instanceof Error) {
    if ('code' in error) {
      if (error.code === 'ENOENT') return { _tag: 'NotFound', path: filePath }
      if (error.code === 'EACCES') return { _tag: 'PermissionDenied', path: filePath }
    }
  }
  return { _tag: 'IoError', cause: error }
}

const readFile = (filePath: string): TE.TaskEither<FileError, string> =>
  TE.tryCatch(
    () => fs.readFile(filePath, 'utf-8'),
    (e) => toFileError(e, filePath)
  )

const writeFile = (filePath: string, content: string): TE.TaskEither<FileError, void> =>
  TE.tryCatch(
    () => fs.writeFile(filePath, content, 'utf-8'),
    (e) => toFileError(e, filePath)
  )

const readJson = <T>(filePath: string): TE.TaskEither<FileError | { _tag: 'ParseError'; cause: unknown }, T> =>
  pipe(
    readFile(filePath),
    TE.chain(content =>
      TE.tryCatch(
        () => Promise.resolve(JSON.parse(content)),
        (e): { _tag: 'ParseError'; cause: unknown } => ({ _tag: 'ParseError', cause: e })
      )
    )
  )

// Usage: Load config with fallback
const loadConfig = () =>
  pipe(
    readJson<Config>('./config.json'),
    TE.orElse(() => readJson<Config>('./config.default.json')),
    TE.getOrElse(() => T.of(defaultConfig))
  )
```

---

## Limitations
- Use this skill only when the task clearly matches the scope described above.
- Do not treat the output as a substitute for environment-specific validation, testing, or expert review.
- Stop and ask for clarification if required inputs, permissions, safety boundaries, or success criteria are missing.
✦ Чат AI