Комментарии 25
Академически — круто:
показано, что можно прервать программу на середине, а потом восстановить выполнение, подсунув тот же стек.
Практически — пока не видно удобства:
вместо обещанного
мы получаем код, который визуально не проще работы с колбеками:
Спасибо автору за расширение моего кругозора, но видимо следующим шагом надо писать свой новый язык JVM, где можно использовать свои собственные ключевые слова async и await.
показано, что можно прервать программу на середине, а потом восстановить выполнение, подсунув тот же стек.
Практически — пока не видно удобства:
вместо обещанного
data = readSocket();
мы получаем код, который визуально не проще работы с колбеками:
Coro coro = Coro.initSuspended((@Async({@Await(value = "yield")}) ICoroRunnable) () -> {
Coro.get().yield();
});
Спасибо автору за расширение моего кругозора, но видимо следующим шагом надо писать свой новый язык JVM, где можно использовать свои собственные ключевые слова async и await.
+3
Если писать сервер, то этот ужасный код будет только в самом верху стека обработки запроса, далее писать код можно обычными методами, только следя за аннотациями на точках восстановления. А все асинхронные операции достаточно один раз обернуть в сопрограммы и положить в утилитный класс, и больше его не трогать, а пользоваться ими как синхронными вызовами. См пример
0
следующим шагом надо писать свой новый язык JVM, где можно использовать свои собственные ключевые слова async и awaitДля желающих они реализованы в стандартной scala-library, на базе Future (в java8 запилили аналогичный, хотя и более простой, CompletableFuture).
+2
Подскажите, а в чем корневое отличие данного подхода от Future и CompletableFuture?
+1
По-моему слишком много магии.
Akka и rx frameworks выглядят гораздо приятнее и надежнее.
Akka и rx frameworks выглядят гораздо приятнее и надежнее.
+2
А чем плох vert.x toolkit?
0
Там нужно писать коллбеки, именно этого и хочется избежать.
0
По-моему проще написать обертку для vert.x, чтобы избежать callback. Разве нет?
0
Из документации vert.x 3: RxJava style APIs if you don't like callbacks.
Думаю это как раз то что нужно чтобы избежать коллбеки.
Думаю это как раз то что нужно чтобы избежать коллбеки.
0
Может быть, я не туда смотрю, но тут всё на коллбеках: github.com/vert-x3/vertx-rx/blob/master/rx-java/src/main/asciidoc/java/index.adoc#async-result-support
0
Я если правильно понимаю так:
Action1<HttpServer> onNext = httpServer -> {};
Action1<Throwable> onError = httpServer -> {};
Action0 onComplete = () -> {};
Handler<AsyncResult<HttpServer>> handler1 = RxHelper.toFuture(onNext);
Handler<AsyncResult<HttpServer>> handler2 = RxHelper.toFuture(onNext, onError);
Handler<AsyncResult<HttpServer>> handler3 = RxHelper.toFuture(onNext, onError, onComplete);
0
Кажется, это те же коллбеки, записанные иначе (заранее сохранённые в отдельную переменную и потом использованные). Но дело даже не в том, что коллбеки плохи сами по себе. Проблема в неудобствах, которые возникают, когда нам нужно результат одной операции передать в следующую. Плюс, надо как-то уметь обрабатывать ошибки. А если писать синхронный код, таких проблем не возникает — всё просто и понятно. В этом суть подхода.
0
Как же данная проблема решается в том же vert.x?
0
Предполагаю, что написанием этого неудобного кода :) не знаю, как решить эту задачу удобно и без сопрограмм.
0
Немного изучил тему. И опять же нашел вариант у то во же vert.x. Для задач, где необходимо выполнить методы последовательно и для проблемы с обработкой ошибок в частности предлагается использовать Vertx-Sync. Они так и пишут:
Не знаю насколько это удобно, тут важно понять окупаются ли все эти неудобства с той мощностью что дает vert.x из коробки.
The non blocking nature of Vert.x leads to asynchronous APIs. Asynchronous APIs can take various forms including callback style, promises or Rx-style. Vert.x uses callback style in most places (although it also supports Rx).
In some cases, programming using asynchronous APIs can be more challenging than using a direct synchronous style, in particular if you have several operations that you want to do in sequence. Also error propagation is often more complex when using asynchronous APIs.
Vertx-sync allows you to work with asynchronous APIs, but using a direct synchronous style that you’re already familiar with.
Не знаю насколько это удобно, тут важно понять окупаются ли все эти неудобства с той мощностью что дает vert.x из коробки.
0
Посмотрел на Vertx-Sync, там написано, что оно работает на базе Quasar, который, как я понимаю, использует аналогичные jcoro механизмы. Ну то есть там тоже есть восстанавливаемый контекст выполнения и инструментирование байт-кода. Так что даже не знаю, что проще — взять одну jcoro или пробовать vertx-sync, который зависит от quasar и vertx, которые сами по себе не маленькие :)
0
Вот, кстати, еще один живой проект github.com/puniverse/quasar, имеющий цели схожие с вашими. Без поддержки на уровне языка, все это все же выглядит не слишком удобно, как мне кажется.
0
В своём проекте TeaVM я тоже сделал сопрограммы, вдохновившись опытом javaflow. Более того, я даже сделал эмуляцию тредов на них (ну а как ещё сделать треды в JS?), вот работающий пример.
В отличие от javaflow код сильно не разбухает, т.к. я произвожу статический анализ всего кода, и могу определить, какие методы никогда не будут вызывать, прямо или косвенно, сопрограммы, и таким образом не транформировать их. Кроме того, я не добавляю код по сохранению состояния метода после каждого invoke'а, а просто вставляю goto на общий код (таким образом получается некоторый оверхед по размеру сохранённого состояния). Так же поддерживаются лямбды, но не поддерживается reflection, потому что сам TeaVM не поддерживает reflection.
Большой проблемой было то, что вставляя развесистый tableswitch в начало метода, мы получаем чаще всего неприводимый граф потока, который нельзя перевести в структурные операторы JavaScript. Эту проблему я решил частично разрезанием графа, частично node splitting'ом.
Вот пример того, как создаются сопрограммы:
Ну а пример использования такого метода:
Соответственно, Complete будет напечатан после содержимого.
К сожалению, всё очень сильно завязано на инфраструктуру TeaVM, и для просто Java работать не будет.
В отличие от javaflow код сильно не разбухает, т.к. я произвожу статический анализ всего кода, и могу определить, какие методы никогда не будут вызывать, прямо или косвенно, сопрограммы, и таким образом не транформировать их. Кроме того, я не добавляю код по сохранению состояния метода после каждого invoke'а, а просто вставляю goto на общий код (таким образом получается некоторый оверхед по размеру сохранённого состояния). Так же поддерживаются лямбды, но не поддерживается reflection, потому что сам TeaVM не поддерживает reflection.
Большой проблемой было то, что вставляя развесистый tableswitch в начало метода, мы получаем чаще всего неприводимый граф потока, который нельзя перевести в структурные операторы JavaScript. Эту проблему я решил частично разрезанием графа, частично node splitting'ом.
Вот пример того, как создаются сопрограммы:
@Async static native String get(String url) throws IOException;
static void get(final String url, final AsyncCallback<String> callback) {
XMLHttpRequest xhr = XMLHttpRequest.create();
xhr.overrideMimeType("text/plain; charset=x-user-defined");
xhr.onComplete(() -> {
if (xhr.getStatus() != 200) {
callback.error(new IOException("Error loading remote resource " + url + ". Status: " +
xhr.getStatus() + " " + xhr.getStatusText()));
return;
}
callback.complete(xhr.getResponseText());
});
xhr.open("get", url);
xhr.send();
}
Ну а пример использования такого метода:
System.out.println("Fetching data...");
System.out.println(get("http://localhost:8080"));
System.out.println("Complete");
Соответственно, Complete будет напечатан после содержимого.
К сожалению, всё очень сильно завязано на инфраструктуру TeaVM, и для просто Java работать не будет.
+2
Круто! Я не решился на статический анализ, решил для начала размечать аннотациями вручную. Статический анализ действительно хорошо работает в этом месте?
0
К сожалению, статический анализ не будет работать в JVM. В TeaVM я сознательно с самого начало убил reflection ради возможности строить method call graph, и исходя из моих целей это было оправдано. Когда потом уже понадобилось делать сопрограммы, method call graph оказался очень кстати.
0
Зарегистрируйтесь на Хабре, чтобы оставить комментарий
JCoro — асинхронность на сопрограммах в Java