All streams
Search
Write a publication
Pull to refresh
23
0
Aleksandr Kondaurov @kondaurovDev

Программер

Send message

ответил вне ветки, сорри :)

Так andThen и не должен работать в случае ошибки

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


Я поторопился просто опубликовать и нужно было подумать над планом статьи.

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

Я понимаю, что во всем самому можно разобраться, но тогда зачем нужны отдельные статьи?

Я опрос закинул на то, интересна эта тема или нет, через неделю посмотрю на результат и будет видно 😃

А можно подробней про compile time? Те если я верно понимаю, что использование эффекта облегчает проброску ошибок нужного типа?

Да, все ошибки пробрасываются на уровень типа эффекта и они известны на уровне написания кода.

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

https://effect.website/docs/error-management/expected-errors/#error-tracking

То есть TypeScript это не просто проверяет синтаксические ошибки а он помогает писать fault tolerant код.

Просто на данный момент она для меня выглядит как просто набор фий на промисах.

Промисы это плохо так как они не типизированы в плане возможных ошибок и исполняются сразу как только создаются

и они вообще не используются, точнее используются только на крайних точка приложения (где мы например запускаем наше приложение).

В конечном итоге эффекты исполняются в файберах, это легковесные промисы. Файберы это как Future в Scale или Корутины в котлине

Вот тут рассказывают про Effect oriented программировании и что такое эффекты вообще

https://www.youtube.com/watch?v=252slbrmk8M&t=4157s

Нет, Effect.andThen работает в случае успеха предыдущего эффекта.

Иронии нет никакой.

https://effect.website/docs/error-management/expected-errors/#short-circuiting

Элементарно, эта логика пишется в вашей обертке для запросов к АПИ

Ну если вам не жалко и интересно тратить свое время на написание оберток, а не решать непосредственно сами задачи и спать спокойно ночью, то это ваш выбор 😀

Все так же элементарно, просто реализуйте это в обертке к АПИ. Пишется 1 раз в жизни, потратить нужно драгоценные 60 минут времени на ретраи и кэш.

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

Что если на одном проекте нужно будет кеширование, а на другом нет? Или на разных проектах будут разные стратегии восстановления по ошибкам? Будете изобретать свой axios? Или будете городить функцию конструктор для вашего клиента? Удачи вам в этом, серебряных пуль как и идеальных http клиентов не бывает

Я ,например, много раз писал обертки на обертки и мне это надоело 🥲 Интереснее решать реальные задачи и не переизобретать колесо чисто технических задач

Ваш путь прятать его под конкретный фреймворк/библиотеку, а так да, это не ваш путь конечно прятать что-то)) Что за лицемерие на лицемерии?)

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

Код использованием Effect является декларативным, он ничего не скрывает. Когда смотришь на эффект то видишь все.

Ваш же код, где использовался cached , скрывает имплементацию, только название cached говорит о том что это про кеширование. А перед кешированием может быть еще таймаут. И кеширование бывает разное, например мы хотим делать invalidate по истечению времени. И вот в таком случае смотришь на ваш cached и не понимаешь детали

Ваш путь писать лапшекод такого вида:

Жесть) Ещё один шаг и вы готовы к максимально убогому и вырвиглазному write-only once RxJS коду, хорошо подумали?)

У вас от количества try catch и await глаза не заболели когда писали свои обертки?

И еще, у вас в catch будет ошибка типа unknown, то есть если вы захотите написать разное поведение для разных случаев то вам дорога только в instance of. Но конечно вы можете все ошибки смешать в кучу и на любые ошибки реагировать одинаково.

Effect позволяет на разные типы ошибок запустить разную логику по восстановлениям с этих ошибок

Не очень понял ваш посыл. Все поставленные вопросы решены из коробки, например, в RTKQuery, которая тоже является библиотекой. Таким образом, примеры и поставленные вопросы являются типовыми на которые уже есть классные решения из коробки.

Я посмотрел что такое RTKQuery , вроде эта либа решает как раз проблемы с кешированием, реакцией на ошибки, восстановлением и тп. Но она заточена под р

Вы можете сказать еще что есть библиотека Zod которая решает проблему с валидацией данных по схеме и тому подобное.

А пока на вид, еще одна библиотека из сотни другой уже выпущенной.

Все это - частные задачи, Effect же дает другой подход вообще в разработке через систему эффектов. Это не очередная TypeScript библиотека, если хотите сравнения с другими то это библиотека является портированной версией Zio библиотеки из Scala.

https://effectorientedprogramming.com/

В коде нет явно написанного Promise но подразумевается, или вы знаете как выполнять Http запросы синхронно? 😃

Ну а по поводу кейсов:

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

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

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

import { Effect } from "effect"
import { HttpClient } from "@effect/platform"

export const getTodo = (
  id: number
) =>
  Effect.gen(function* () {
    const client = yield* MySmartHttpClient;
    const result = yield* client.get(`/todos/${id}`).pipe(
      Effect.andThen((response) => response.json)
    );
    return result;
  });

const MySmartHttpClient =
  Effect.gen(function* () {
    const client = yield* HttpClient.HttpClient;
    client.pipe(
      HttpClient.retry({
        while: error => error._tag == "ResponseError" && error.response.status === 500,
        times: 3
      })
    )
    return client;
  })

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

const cachedTodo = cached( getTodo, 5000 )

Ну во первых, я, как читатель этого кода, не понимаю сигнатуру функции cached.

Во вторых, вы написали тот самый рутинный код и сделали код менее читаемым, а вот как это было бы на Effect

import { Effect } from "effect"
import { HttpClient } from "@effect/platform"

const getTodo = (
  id: number
) =>
  Effect.andThen(
    HttpClient.HttpClient, 
    httpClient =>
      httpClient.get(`/todos/${id}`).pipe(
        Effect.andThen((response) => response.json),
        Effect.cachedWithTTL("5 seconds")
      )
  )


Ну во первых, декораторы находятся уже несколько лет как в experimental stage и все никак не дойдут до стабильной.

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

Я не использовал декораторы в TypeScript но прекрасно понимаю идею, как они применяются в Java Spring например, это просто магия, код может падать в runtime просто если забыть добавить какой нибудь декоратор. Это просто Runtime механизм, а Effect это про Compile time

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

Это справедливое замечание

Просто этот пример немного некорректный еще, потому что нигде не указано а что за httpClient , просто для наглядности его вынесли за скобки.

Вообще полный код будет выглядеть так и будет сразу понятно что возвращается эффект, даже если читаешь pull request с кодом

import { Effect } from "effect"
import { HttpClient } from "@effect/platform"

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

// const getTodo: (id: number) => Effect.Effect<unknown, HttpClientError, HttpClient.HttpClient | Scope>

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

import { Effect } from "effect"
import { HttpClient } from "@effect/platform"

const getTodo = (
  id: number
) =>
  Effect.gen(function* () {
    const client = yield* HttpClient.HttpClient;
    const result = yield* client.get(`/todos/${id}`).pipe(
      Effect.andThen((response) => response.json)
    );
    return result;
  });

// const getTodo: (id: number) => Effect.Effect<unknown, HttpClientError, HttpClient.HttpClient | Scope>

Да, это заслуга TypeScript и его способности выводить типы.

Effect был бы не таким привлекательным если бы не было языка со строгой статической типизацией

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

Сам главный сайт Effect намного круче чем моя статья, я не спорю.

const getTodo = (
  id: number
): Effect.Effect<
  unknown,
  HttpClientError | TimeoutException
> =>
  httpClient.get(`/todos/${id}`).pipe(
    Effect.andThen((response) => response.json),
    Effect.scoped,
    Effect.timeout("1 second"),
    Effect.retry({
      schedule: Schedule.exponential(1000),
      times: 3
    }),
    Effect.withSpan("getTodo", { attributes: { id } })
  )




И откуда же должно стать очевидно, что httpClient.get() не выполняет запрос, а возвращает эффект? 

Effect это TypeScript библиотека

const getTodo = (
  id: number
): Effect.Effect<unknown, HttpClientError> => //  Вот отсюда, Typescript тип
  httpClient.get(`/todos/${id}`).pipe(
    Effect.andThen((response) => response.json)
  )

У большинства современных разработчиков скорее будет предположение, что httpClient.get() выполняет запрос и возвращает промис.

Ну если такие TypeScript разработчики не видят тип после двоеточия то им никакие эффекты вместе с промисами не помогут 😀

Effect это не для реактивного программирования а скорее функционального.

Очередная библиотека для реактивного программирования, а чем она лучше того же RxJS? Быстрее? Понятнее? Или всё вместе?

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

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

по сути каждая такая библиотека - это отдельный язык внутри языка и буквально приходится сидеть с документацией

это не отдельный язык внутри языка. Да, у Effect богатый API набор но все ведь написано на TypeScript, и все функции Effect имеют довольно примитивные названия, типа sleep, repeat, catch, andThen, die

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

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

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

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

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

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

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

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

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

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

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

Конечно вам не нужен докер, вы же даже не можете почитать документацию.

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

Это вы про --ssh парамерт говорите который появился в 18.09? Я всегда пользовался multistage билдом чтобы убедиться что ничего не попало в слой. Добавление --ssh это просто маркетинг, но это мое мнение, не хочу затронуть чувства верующих.
Почти вся статься показывает, что 3 года знакомства с докером это docker ps и docker run.

Статья показывает мою боль с которой я столкнулся при использовании docker. Это моя история и ничего плохого в том что я делаюсь ей — нет.
Я игрался с k8s, c docker-swarm, с docker-daemon, docker-machine. Не подскажите какой там следующий docker-*?
Docker-compose это просто python скрипт который дергает docker daemon api через другую python либу. Тоже самое использует Ansible, только с помощью него можно сделать что угодно и где угодно (создать директории, спулить образы, тп) + запустить docker контейнеры.

Вопрос: Зачем изучать docker-compose если есть инструмент с более широким применением?
Далее, какие есть варианты запуска контейнера на удаленной машине? (вангую сейчас скажете про docker swarm и его поддержку docker compose формата)

Когда то нравился compose, но сейчас нет, потому что это оверхед над docker api. Натыкались на такие issue? github.com/docker/compose/issues/3574

а вы про yaml… Тоже, скорее всего, технология с ограниченным применением не заслуживающая изучения документации.

Мне нравится декларативное описание в yaml. Он намного лучше json
Автор совсем недавно открыл туннелирование и ВПН и не хочет изучать документацию к инструменту с которым работает,

Впн я давно использовал в компаниях где были админы и настраивали его сами. Про ssh туннели я не знал, да, но как то пользовался пробросом портов в `kubectl`, думал это специфичная штука кубера
Задумывался и спорил по этому много ни раз с другими людьми. И про CI/CD я спорил, что это такое и как должно работать. Но потом, когда устал спорить, я понял, что все это бессмысленно, потому что каждый понимает по своему.
Теперь я смотрю только на результат, без разницы какой термин у процесса. Так же как и на docker я смотрю сейчас, что он умеет делать и как добиться определенного результата.
А я и не говорил что оно работать будет, а сказал что будет упаковано в docker образ.
Очевидно что нужно подготавливать образ чтобы заработало
FROM alpine

RUN wget -O my-project http://ci-cerver/master/my-project.tgz

ENTRYPOINT ["app"]
Стандарт до тех пор пока не появится точно такой же инструмент как docker только более удобный в использовании. Потом все дружненько будут паковать свои приложения еще и под технологию X.

Information

Rating
Does not participate
Registered
Activity