Комментарии 29
Из истории futures в javascript можно сделать вывод, что futures вводят в язык только для того, чтобы следом ввести в язык async/await и опять писать последовательный псевдо-синхронный код.
К сожалению, синтаксический сахар в языках программирования — это плохая идея. Как модная фича — это выглядит круто, но если смотреть на возможности дальнейшего развития языка, каждая языковая фича является якорем, который тянет на дно. Языки, в которых миллион клевых фич очень тяжело выучить, еще тяжелее развивать и поддерживать. И рано или поздно язык достигнет точки в которой он перестанет развиваться или выкинет обратную совместимость и кучу фич, которые у него были.
Пример? Было такое в истории? Или это гипотеза ваша — про выкидывания «кучи фич»?
В языках есть фичи, которые негласно считаются плохими: assert, goto, множественное наследование. Их не используют (если по-хорошему), но язык обязан их поддерживать.
Если взять С++, фич у него невероятно много, по-моему, его можно считать одним из самых сложных, если не самым. Да, он жив и он развивается, но кто сейчас захочет его изучать? Когда есть D, Rust, Go, которые предоставляют примерно те же возможности но на много безопаснее и проще, там где нужно залезть глубже можно взять чистый Си, который довольно простой.
Но сейчас к примеру начали применять такой цикл (в Javascript):
— Фича вводится не через Стандарт Языка, а через плагин (который транспалирует эту новую фичу в старый Стандартный код).
— Если фича в течении НЕ короткого времени (уж по крайней мере больше года) зарекомендовала себя положительно и ей многие с охотой пользуются, то тогда рассматривается вопрос о включении её в Стандарт Языка.
Такой алгоритм позволяет постоянно наращивать фичи в Языке и хоть как-то избегать «опасных фич» (страховаться от них), от которых захочется избавиться в Стандарте по крайней мере в ближайшее время.
В случае с babel уже нету особо смысла в стандарт языка что-то добавлять. Хочешь использовать какую-то фичу — ставишь как плагин к babel, а дальше транслируешь в ES5. Смысл расширять стандарт, если все и так будет транслироваться в ES5?
На данный момент, я считаю, что все новые фичи должны добавляться только через новые api, а не через расширение языка.
Смысл неясен. Но пока такой:
Цикл (Начало: Стандарт = ES5; изменение: Стандарт +1):
Новая фича (введённая в Стандарт + 1) траслируется в Стандарт
Броузеры подтягиваются к (Стандарт + 1)
Повторить.
Бонус -> Броузеры имеют возможность реализовать нативно(!) то, что уже попадёт в Стандарт.
>На данный момент, я считаю, что все новые фичи должны добавляться только через новые api, а не через расширение языка.
С этим пока неясно. По крайней мере это не практикуется. Стандарт Языка расширяется.
Блокирующие интерфейсы действительно плохо сопрягаются с асинхронными. В Baratine блокирующий внешний web-service обертывают в асинхронный микро-сервис и аннотируют его аннотацией @Workers(). @Workers регулирует количество потоков для такого сервиса-обёртки.
@Workers(32)
@Service
public class MyJAXWSServiceWrapperImpl implements MyJAXWSServiceWrapper {
public void execute(Result<Reply> result) {
Reply reply = ...// obtain reply in a blocking call to external service
result.ok(reply);
}
}
Таким образом код, вызывающий обёртку, будет соответствовать идиоме асинхронного кода.
Передав Result следующему сервису метод выходит и освобождает микро-сервис для следующего вызова. Это соответствует второму принципу в списке.
Передача Result в следующий сервис необходима потому, что Result ещё не готов, а следующий сервис и должен его заполнить. Передав Result следующему сервису метод выходит и освобождает микро-сервис для следующего вызова.
Я бы утверждал, что профита никакого. Из интерфейса не совсем понятно, кто за что отвечает. Вообще, некоторые принципы функционального программирования учат хорошему стилю. Например, не использовать изменяемые данные и писать "чистые" функции. Неследование этим принципам может добавить только головной боли. Не знаком с описанным в статье фреймворком, но кажется, что вполне можно было бы написать что-то подобное:
public interface CreditService {
Result<PaymentStatus> pay(int amount, CreditCard card);
}
Создание сущности Result<PaymentStatus>
вне вызова метода, по-моему, только усложняет код. Метод pay
должен быть в состоянии самостоятельно позаботиться о создании сущности Result<PaymentStatus>
. И, кстати, такой подход лучше описывает интерфейс. Вполне очевидно, что в результате вызова метода мы получим отложенный результат и что метод сам по себе неблокирующий. Используя же в сигнатуре void
не говорит ни о чем. void
, вообще, должен только указывать на функции с побочными эффектами, что, в принципе, должно использоваться как можно реже.
Асинхронный стиль программирования не вписывается полностью в привычные идиомы и правила, но с его развитием появятся дополняющие его идиомы и шаблоны.
В данном фреймворке создание Result<> клиентом является идиомой.
Так, например, в цепочке из двух сервисов где клиент вызывает сервис Foo, а сервис Foo вызывает сервис Bar создание сущности Result<> производится в коде клиента (RestHello) и выглядит верным подходом.
@Service
public class Foo {
@Service @Inject
Bar _bar;
public void foo(Result<String> result) {
_bar.bar(result.then());
}
}
@Service
public class Bar {
public void bar(Result<String> result) {
result.ok("Hello World!");
}
}
public class RestHello {
@Service @Inject
Foo _foo;
@Get
public void hello(RequestWeb request) {
_foo.foo(request.then()); //request.then() создаёт сущность Result для передачи в foo и
//ставит себя во главу цепочки принимающей результат. По готовности результата
//RequestWeb отсылает результат удалённому клиенту
}
}
Кроме того, Result является функциональным интерфейсом и может быть использован в стиле лямбда выражения:
_foo.foo(request.then((value, r)->r.ok(value)));
Посмотрите как можно сделать асинхронность:
https://twitter.github.io/finagle/guide/Futures.html
_creditService
_foo
_bar
Вот Вы зачем так переменные называете (C-style похоже)? Нас же могут читать молодые неокрепшие умы…
service.doSomething(param)
.thenApply(result -> transform(result))
.thenAccept(result -> log.info(result));
Принуждение к асинхронности в Java сервисах для Baratine