4

コールバックの代わりに Deerrable を使用する利点は何ですか。小さな例。

# This is Deferrable interface
request = NetworkIO.get
request.callback do |resp|
  puts "Job is done"
end
request.errback do |err|
  puts "Oh. My name is Forest, Forest Gump"
end

# And this is callback interface
NetworkIO.get do |resp|
  if Exception === resp
    puts "Oh. My name is Forest, Forest Gump"
  else
    puts "Job is done!"
  end
end

# Or node js style
NetworkIO.get do |err, resp|
  if err
    puts "Oh. My name is Forest, Forest Gump"
  else
    puts "Job is done!"
  end
end

Deerrable には 2 つのネスト レベルがあり、callbacks には 3 つのネスト レベルがあります。

ご覧のとおり、Deferrable オブジェクトを使用すると、たとえば errback を渡し、コールバックのみを定義できます。これは場合によっては意味があるため、コード行とネストを減らすことで、コードが読みやすくなります。

しかし。厄介なケースを見つけました。たとえば、この偽の非同期 API を取得したとします。

class SomeIO
  def find(msg)
    response = DeferrableResponse.new
    req = socket.send(msg)
    req.callback do |data|
      response.succeed(data)
    end
    req.errback do |err|
      response.fail(err)
    end
    response
  end

  def count(msg)
    response = DeferrableResponse.new
    req = find(msg)
    req.callback do |data|
      response.succeed(data.size)
    end
    req.errback do |err|
      response.fail(err)
    end
    response
  end
end

コールバック モードでどのように見えるか

class SomeIO
  def find(msg, &blk)
    socket.send(msg) do |resp|
      blk.call(resp)
    end
  end

  def count(msg, &blk)
    find(msg) do |resp|
      if Exception === resp
        blk.call(resp)
      else
        cnt = resp.size
        blk.call(cnt)
      end
    end
  end
end

今でも少しきれいに見えます(私の好みでは)。しかし、それは鑑賞主観です。非同期 API よりも同期 API をサポートすることを想像してみてください。Deferrable Interface を使用すると、遅延可能な応答を持つすべてのメソッドをファイバーにラップする必要があります (これは非常に大きな作業であり、サポートするのは非常に重いです)。

class SyncSomeIO < SomeIO
  def find(msg, &blk)
    fib = Fiber.current
    socket.send(msg) do |resp|
      fib.resume(resp)
    end
    res = Fiber.yield
    raise res if res === Exception
    res
  end
end

つまり、ソケットをファイバーでラップしただけで、非同期コードが同期化されました。すべてのブロックメソッドも再定義する必要があると本当に言いました:

class SomeIO
  ...

  def count(msg, &blk)
    find(msg) do |resp|
      if Exception === resp
        block_given? ? blk.call(resp) : resp
      else
        cnt = resp.size
        block_given? ? blk.call(cnt) : cnt
      end
    end
  end
end

これはマイナーな変更ですが、わずか数行のコードで API を同期モードと非同期モードの両方で動作させることができます。

このような大きな紹介で申し訳ありませんが、読んでいただきありがとうございます(2人全員)。

質問は。Deferrable is de-facto は、Ruby のイベント API の標準です。だから多分私は何かを誤解していて、Deferrableを間違った方法で使用していますか? コールバック インターフェイスの臭いがして、何か悪い問題が発生している可能性がありますか?

PS: EventMachine で MongoDB ドライバーに取り組んでいて、クライアントに同期インターフェイスを追加しているため、これをすべて書きました。そして最終的に、すべてのパブリック API にモンキー パッチを適用して同期サポートを追加する必要があることに気付きました。これは、Deferrables と、コールバックでそれを書き直すことを考えているためです。

4

1 に答える 1

1

のインターフェイスを制御する場合Deferrable、次の行に沿って何かを行うことができます。

class DeferrableResponse
  def sync
    fiber = Fiber.current

    callback { |val| fiber.resume(val) }
    errback { |err| fiber.resume(err) }

    result = Fiber.yield
    raise result if result.is_a? Exception
    result
  end
end

これにより、次のように同期 API を使用できるようになります。

response = NeworkIO.get.sync
于 2013-06-24T07:34:43.520 に答える