socketry / async

An awesome asynchronous event-driven reactor for Ruby.
MIT License
2.14k stars 87 forks source link

Async documentation #73

Closed bruno- closed 1 year ago

bruno- commented 4 years ago

Hi,

@ioquatix as we talked on the call the other day, here's my list of questions about async that I wish I could have read somewhere so I can get up to speed faster. I'll add question for any async-* gem, not just core async.

Anyone can add their own questions. These will be used when building the docs for async gems.

Note: I don't expect answers to these questions. I just think it would be useful to document this stuff.

Related: here's another ruby async project that has decent webpage and docs https://digital-fabric.github.io/polyphony/

async gem

barrier = Async::Barrier.new
semaphore = Async::Semaphore.new(2)

# Example 1
semaphore.async(parent: barrier) do
  # ...
end

# Example 2
barrier.async(parent: semaphore) do
  # ...
end

Which of the two examples above is right? If both are right, when do I use one and when do I use the other?

require 'async'

Async do |task| # reactor exits immediately, not what I expected
  task.reactor.after(1) do
    puts "Example 1" # never runs, not sure why
  end
end

Async do |task|
  task.reactor.sleep(1)
  puts "Example 2" # this one runs, all good
end

Async do |task|
  task.async do |subtask|
    subtask.sleep 1
    puts "Example 3" # this one runs, all good
  end
end

Async do |task| # reactor exits immediately, not what I expected
  task.reactor.every(1) do
    puts "Example 4" # never runs, I expect this to run forever
  end
end

async-io

async-http

Questions related to all async gems

jsaak commented 3 years ago

I want to integrate a non-blocking C API with the Reactor. I found Reactor#register, i think it may be able to do it, but i have no idea how. I working example would be delightful. WITH error handling. Do not just ignore error handling, since that is the most difficult part with these tasks usually.

trevorturk commented 1 year ago

Just a note to say that https://github.com/socketry/async-examples has a bunch of example code which may be useful to people just getting started with Async, especially when used in conjunction with GraphQL.

jsaak commented 1 year ago

How do you implement a function like Open3.capture3() ? Using open3 does not seem to work, however it is a part of the standard library!

This does not work either:

  pipe_error_r, pipe_error_w = IO.pipe
  pipe_error_w.close
  pipe_stdout_r, pipe_stdout_w = IO.pipe
  pipe_stdout_w.close

  pid = spawn(commandline, :err => pipe_error_r, :out => pipe_stdout_r)

  err = ''
  Fiber.schedule do
    while line = pipe_error_r.gets do
      err += line
    end
  end

  stdout = ''
  Fiber.schedule do
    while line = pipe_stdout_r.gets do
      stdout += line
    end
  end

  pid, status = Process.wait2 pid

  if status.exitstatus != 0
      puts "exitcode: #{status.exitstatus}, stderr: #{err}"
  end
ioquatix commented 1 year ago

You will need to put the entire thing into a Fiber if you want Process.wait2 to be non-blocking and allow other fibers to execute.

daipom commented 1 year ago

Thanks for the documentation!

I saw Getting Started, and I felt the examples of Creating an Asynchronous Tasks and Waiting for Results are misleading.

https://github.com/socketry/async/blob/c5911ec9b0e4dca9896cf0b4063d71c77e5a614e/guides/getting-started/readme.md?plain=1#L21-L47

When I saw these examples, I understood that the blocks being passed to the Async run asynchronously. However, it was wrong and the blocks run synchronously, right? (The top-level Async is executed synchronously, not Asynchronously)

I'm not sure what would be a better example of asynchronous execution in Getting Started, but I'm just sharing what I felt after reading Getting Started for now.

ioquatix commented 1 year ago

I believe most of these questions (at least, the async specific ones) are covered by the updated documentation.

However, there are some specific questions I'll answer here.

Question related to semaphore and barrier use (the order)

I believe it's better to put the barrier at the end of the chain. However, I don't think it matters very much in practice.

I would like more info on how #every, #after and #sleep methods work.

Those methods are now removed and shouldn't be used, they were dangerous because they ran as bare timers. We will probably eventually remove the dependency on the timers gem too.

Why don't you just monkey patch core ruby classes? Why did you go with wrappers? What are the monkey patching pros and cons?

Async 2+ uses hooks implemented in the Ruby language and the wrappers are no longer required.

How do you implement a function like Open3.capture3()?

In Async 2+, you can use Open3 without any special consideration and it is internally non-blocking.

ioquatix commented 1 year ago

Also, sorry for replying 3 years later :)

ioquatix commented 1 year ago

A few more points about async-io: