Hacker News new | past | comments | ask | show | jobs | submit login

That seems kinda verbose to use. It's also a bit suspicious that all the examples discard the results; is it hard to get the results back out?



Just tried it out. Async blocks evaluate to Task objects which have a wait method and a result attribute which evaluates to the value of the block.

    require 'async'
   
    res = Async do |task|
     name_task = task.async do 
       sleep 2
       "Jenny"
     end

    task.async do
      sleep 5
      9
    end

    "Hello #{name_task.wait}"
   end

   puts res  # => "Hello Jenny" after 5 seconds.
After using Python's and JS's async implementations this seems beautiful by comparison. Here's the a rough Python equivalent.

    import asyncio


    async def get_name():
        await asyncio.sleep(2)
        return "Jenny"


    async def get_number():
        await asyncio.sleep(5)
        return 7


    async def main():
        number_task = get_number()
        name_task = get_name()

        name, _ = await asyncio.gather(name_task, number_task)

        return f"Hello {name}"


    print(asyncio.run(main()))


Also, it looks like there's a getter for the "current" task (https://socketry.github.io/async/guides/getting-started/inde...), sort of like Thread.current.

Which means that you can just write a whole normal Ruby program, that just uses Task::Async.current.async(...) wherever it likes to schedule subtasks (sort of like calling spawn/3 in Erlang), and then treat them as regular futures, even returning the future out of the current lexical scope without thunking it; and then have exactly one Async block at the toplevel that kicks off your main driver logic and then #wait s on it. All without having to pass the current task down the call stack everywhere.

(And if you want to schedule a bunch of stuff to happen in parallel and then wait for it all to be done, but you're below the toplevel Async block, you'd do that by scheduling the subtasks against an Async::Barrier: https://socketry.github.io/async/guides/getting-started/inde...)


> Which means that you can just write a whole normal Ruby program, that just uses Task::Async.current.async(...) > All without having to pass the current task down the call stack everywhere.

Yes, you can also use `Async { work }` instead of `Async::Task.current.async { work }`.


> is it hard to get the results back out

It's trivially easy to get the results, here's a quick example:

    require "async"
    require "open-uri"

    results = []

    Async do |task|
      task.async do
        results << URI.open("https://httpbin.org/delay/1.6")
      end

      task.async do
        results << URI.open("https://httpbin.org/delay/1.6")
      end
    end


I'm not sure if this is the most realistic example since you're implicitly relying on this being at the top level and there being a global await at the end of the block. Surely any real program will have all the work done inside a single top-level event loop.

    require 'async'

    Async do
        results = []

        Async do
          sleep 1
          results << "Hello"
        end

        puts results  # => []
    end


I just ran your example and I'm getting `puts results` line to output "Hello", just as the program intends. I'm not sure why you're getting a different result.

In any case, I'm assuring you: getting the results "out of tasks" is trivially easy.

> Surely any real program will have all the work done inside a single top-level event loop.

I don't get this. Can you please explain more what you have in mind and I'll try to help clarify things.


Sorry, I needed to add a sleep to get the behavior I wanted. Now it just prints nothing.

What I mean is I assume any real program is not going to be creating and destroying event loops any time they want to do something Async and that they'll essentially run main in a top level Async do. In fact it seems like that's the only safe thing to do with this library because the following snippet changes semantics depending on whether it's nested in an existing event loop or not.

    results = []
    Async do
      sleep 10
      results << "Hello"
    end
    puts results
So it seems like you'll pretty much always have to have an explicit wait before you can get your results in 99% of cases.


Wrap the example in a `Sync` block to get deterministic results. Now you don't have to worry if the code gets nested in an existing event loop.

    Sync do
      results = []
      Async do
        sleep 10
        results << "Hello"
      end
      puts results
    end


Every task is a promise you can wait on for the result. Concurrent fan out is trivial.




Join us for AI Startup School this June 16-17 in San Francisco!

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: