Обо мне

Привет. Я разработчик и последние три года пишу на TypeScript, делаю свои Pet проекты, смотрю на технические тренды и пытаюсь делать полезные проекты и публиковать их в OpenSource.

Сразу к делу

Вот уже как год я пишу код и использую этот пакет Effect. Для меня Effect является швейцарским ножом который позволяет решать прикладные задачи без написания рутинного но очень важного кода, такого как:

  • работа с ошибками и восстановление из этого состояния (catch)

  • кеширование результатов дорогих вычислительных операций

  • параллельное выполнение процессов, остановка (interrupt)

  • работа с состоянием и безопасное изменение этого состояния

  • создание схем типов данных, чтение/запись по схеме, и т.п.

  • и многое другое

Effect состоит из большого количества небольших модулей и помогает писать код в декларативном стиле. Например в этом пакете можно найти следующие модули:

  • Array

  • HashMap

  • String

  • и десятки других...

Казалось бы зачем нужен Array ведь он есть в стандарте JS, но он нужен когда нужно создать такие типы как NonEmptyReadonlyArray, или другой неизменяемый Array который по сути Tuple

Effect позволяет мне, как TypeScript разработчику, держать фокус на главной части проекта и не распыляться на чисто технические задачи никак не связанные с целью проекта.

При чем тут эффекты?

Effect это не только швейцарский нож в котором есть все, остается просто найди нужную функцию ?.

Effect это еще идея про то как можно и нужно писать программы, которые легко писать, читать, поддерживать.

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

Эффект это значение, как например можно создать const foo = "bar" где foo будет содержать значение "bar".

Например:

const getTodo = (
  id: number
): Effect.Effect<unknown, HttpClientError> =>
  httpClient.get(`/todos/${id}`).pipe(
    Effect.andThen((response) => response.json)
  )

Тут описывается функция, которая создает эффект. Важно понимать, что когда функция getTodo возвратила эффект то она не выполнит http запрос, она лишь вернет эффект, который умеет посылать http запрос и он может завершиться значением с типом unknown или завершиться с ошибкой типа HttpClientError.

Так как эффект это значение то это открывает дополнительные возможности и можно легко создать другой эффект на базе этого который будет:

  • восстанавливаться если была ошибка типа HttpClientError

  • попытаться повторно выполнить эффект несколько раз в случае определенной ошибки

  • кешировать результат

  • множество других сценариев...

Про то, что такое эффект можно на примерах найти тут.

Effect сообщество пишет отличную документацию по многим пунктам.

Пока на этом все

Сорри что статья короткая.

Я не нашел на хабре статьи которая рассказывала про эту мощную библиотеку. Цель этой статьи в том чтобы заинтересовать TypeScript разработчиков и попробовать использовать ее в своих задачах.

Я могу подробнее написать в следующий раз статью про свой опыт работы с Effect в более подробной форме. У меня нет цели написать туториал или что то подобное, потому что Effect сообщество очень круто справляется с этой задачей

Ссылки

главный сайт

youtube

как работать с ожидаемыми/не ожидаемыми ошибками

мои библиотеки которые используют Effect: https://github.com/orgs/effect-ak/repositories

Комментарии (46)


  1. nin-jin
    28.01.2025 21:13

    А простой и понятный код писать - слишком не спортивно?

    const getTodo = ( id: number )=> httpClient.get( `/todos/${id}` ).json()


    1. kondaurovDev Автор
      28.01.2025 21:13

      Это конечно моя ошибка что привожу такие примеры, что люди их интерпретируют потом так, типа смотрите! Я переписал ваш пример на Vanilla JS и все, пытаться найти для себя что то новое я не буду, и так все знаю

      Promise это по сути тот же `Effect<unknown, any, never>`

      Проблема с Promise в том, что он исполняется (eagerly executed) сразу когда функция getTodo запускается

      Мне ваш код нравится тоже, да, наглядно и красиво но он будет работать только в идеальных условиях, когда backend будет работать без ошибок по контракту и сеть никогда не упадет между вами и backend.

      Разработчики кода понимают, в вызовах внешних сервисов может много чего пойти не так. Даже в вызовах функций нельзя доверять, потому что функции могут кинуть ошибку через throw в любой момент и нужно быть готовым к такой ситуации.

      Возвращаясь к вашему прекрасному коду:

      • что вы будете делать, если ваш backend будет иногда падать с 500 ошибкой и в таком случае нужно просто повторить запрос и ,например, пытаться не больше 3х раз с экспоненциальным разрывом между попытками а потом завершиться с ошибкой?

      • Что делать если вы захотите кешировать результат и повторять запрос если проходит больше 5 минут, скажем?

      Я могу много придумать сценариев но не вижу смысла, я привел 2 примера. Это не сложно написать код который будет кешировать и тп, просто это рутинный код который пишется на многих проектах, которые гарантируют определенный результат

      И пишется такой рутинный где то хорошо, где то плохо, и от этого падает читабельность кода на уровне проектов. Такой рутинный код всегда будет, можно спрятать это под фреймворки, но это не мой путь написания хороших приложений


      1. nin-jin
        28.01.2025 21:13

        В моём коде нет Promise. По поводу кейсов:

        • Первый реализуется в самом httpClient (иначе зачем он вообще нужен?)

        • Второй, вы не поверите, реализуется так:

         const cachedTodo = cached( getTodo, minute(5) )