[
https://issues.apache.org/jira/browse/GROOVY-9381?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18034784#comment-18034784
]
Jochen Theodorou commented on GROOVY-9381:
------------------------------------------
h3. defer keyword
I could see a transform doing that.. I think once we have async/await we can do
the transform and play around with the concept to get a better feel. I see how
defer could be based on async/await, but it would use potentially only a small
portion of it and it could be based on other things... continuations for
example, try-finally for example, try-with-resources for example... But I am
currently not seeing the async/await concept gaining here. Maybe somebody else
knows better?
h3. Reactive Programming
Let me call Reactive Programming for short RP. RP is trying to establish a
"push value changes" kind of logic, while most of what we use is actually pull
based - in the primitive form at least. So if you have {*}def a = b{*}, you
"pull" the value of b and "push" it to a. but the control is in the pull part,
making this a pull logic. In a push logic we would have something like
{*}b.then\{a=it\}{*} This b would be a Promise. For RP we need Observables,
which can emit multiple values. Let us assume we had a similar simple
StreamPromise. A StreamPromise provides a stream of values once a then callback
exists and may or may not complete. {*}b.then\{println it}{*} would then, each
time a value has been made available by b. print it to the console. If we also
add a unsubscribe and allow for multiple then, then we are near an Observable.
A poor one, without operations like map and filter. Adding those we are getting
near a core element for RP. Now each framework has its own Observable, so each
library would require some API to convert the StreamPromise to their
Observable. But the core Problem is more that we did in this proposal not
define such a StreamPromise. It comes a bit into play in the "async for" part,
but only touching the surface.
Really the hard part in RP is declaring all the plumbing and not having proper
stack traces and questionable debugging. "Fixing" that requires a lot more hard
thinking. Of course we can think of taking something like this
{code:Java}
Flux<String> flux = Flux.generate(
() -> 0,
(state, sink) -> {
sink.next("3 x " + state + " = " + 3*state);
if (state == 10) sink.complete();
return state + 1;
}, (state) -> System.out.println("state: " + state));
{code}
and create
{code:Java}
def step(state, yield) {
yield "3 x " + state + " = " + 3*state
if (state == 10) yield.complete();
state + 1;
}
Flux<String> flux = Flux.generate(
{0}, this::step, (state) -> println("state: " + state));
{code}
But we do not gain much and it does not use async/await. We can go further:
{code:Java}
def step(state, yield) {
yield "3 x " + state + " = " + 3*state
if (state == 10) yield.complete();
state + 1;
}
Flux<String> flux = Flux.generate({0}, this::step);
def state = await flux
println("state: " + state)
{code}
But I think here we move already away from RP. And we process here only one
instead of all 10 values.
{code:Java}
def step(state, yield) {
yield "3 x " + state + " = " + 3*state
if (state == 10) yield.complete();
state + 1;
}
Flux<String> flux = Flux.generate({0}, this::step);
for await (state in flux) {
println("state: " + state)
}
{code}
Would solve the problem of not consuming everything.
{code:Java}
def step(state, yield) {
yield "3 x " + state + " = " + 3*state
if (state == 10) yield.complete();
state + 1;
}
Flux<String> flux = Flux.generate({0}, this::step);
{async ->
for await (state in flux) {
println("state: " + state)
}
}()
{code}
Maybe as a way to detach the Closure? And to split the subscribe from the
reactor part? I must say I think I lack enough RP experience to tell if this is
getting us somewhere. You could be of much help here [~glaforge]
> Support async/await like ES7
> ----------------------------
>
> Key: GROOVY-9381
> URL: https://issues.apache.org/jira/browse/GROOVY-9381
> Project: Groovy
> Issue Type: New Feature
> Reporter: Daniel Sun
> Priority: Major
>
> Here is an example to show proposed syntax and backend API(Java's
> {{CompletableFuture}} or GPars's {{{}Promise{}}}), but I think it's better
> for Groovy to have its own {{Promise}} to decouple with Java API because
> async/await as a language feature should be as stable as possible.
> {{async}} will generate the {{Awaitable}} instance such as Groovy {{Promise}}
> implementing the {{Awaitable}} interface, and {{await}} can wait for any
> {{Awaitable}} instance to complete and unwrap it for the result.
> {code:java}
> /**
> * 1. An async function that simulates a network API call.
> * The 'async' keyword implies it runs asynchronously without blocking.
> */
> async fetchUserData(userId) {
> println "Starting to fetch data for user ${userId}..."
>
> // Simulate a 1-second network delay.
> Thread.sleep(1000)
>
> println "Fetch successful!"
> // The 'async' function implicitly returns a "CompletableFuture" or
> "Promise" containing this value.
> return [userId: userId, name: 'Daniel']
> }
> /**
> * 2. An async function that uses 'await' to consume the result.
> */
> async processUserData() {
> println "Process started, preparing to fetch user data..."
>
> try {
> // 'await' pauses this function until fetchUserData completes
> // and returns the final result directly.
> def user = await fetchUserData(1)
>
> println "Data received: ${user}"
> return "Processing complete for ${user.name}."
>
> } catch (Exception e) {
> return "An error occurred: ${e.message}"
> }
> }
> // --- Execution ---
> println "Script starting..."
> // Kick off the entire asynchronous process.
> def future = processUserData()
> // This line executes immediately, proving the process is non-blocking.
> println "Script continues to run while user data is being fetched in the
> background..."
> def result = future.get()
> println "Script finished: ${result}"
> {code}
> Use async/await with closure or lambda expression:
> {code}
> // use closure
> def c = async {
> println "Process started, preparing to fetch user data..."
>
> try {
> // 'await' pauses this function until fetchUserData completes
> // and returns the final result directly.
> def user = await fetchUserData(1)
>
> println "Data received: ${user}"
> return "Processing complete for ${user.name}."
>
> } catch (Exception e) {
> return "An error occurred: ${e.message}"
> }
> }
> def future = c()
> {code}
> {code}
> // use lambda expression
> def c = async () -> {
> println "Process started, preparing to fetch user data..."
>
> try {
> // 'await' pauses this function until fetchUserData completes
> // and returns the final result directly.
> def user = await fetchUserData(1)
>
> println "Data received: ${user}"
> return "Processing complete for ${user.name}."
>
> } catch (Exception e) {
> return "An error occurred: ${e.message}"
> }
> }
> def future = c()
> {code}
--
This message was sent by Atlassian Jira
(v8.20.10#820010)