Skip to content

Commit

Permalink
Fix #1
Browse files Browse the repository at this point in the history
  • Loading branch information
Yacine Petitprez committed Jul 5, 2018
1 parent 93f0148 commit fe7e45c
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 6 deletions.
13 changes: 12 additions & 1 deletion README.md
Expand Up @@ -2,7 +2,6 @@

Add `await` and `async` keyword in Crystal.


## Usage

- Call `async` on any method or block to create a MiniFuture
Expand All @@ -12,6 +11,18 @@ Add `await` and `async` keyword in Crystal.
Can improve drastically application which relay on blocking IO like web API
or file writing.

### await(timeout, future)

```crystal
f = async check_website
begin
await 5.seconds, f
rescue MiniFuture::TimeoutException
# rescue from timeout
end
```

### async_lp

By default, async call the newly created fiber just after creation.
Expand Down
22 changes: 22 additions & 0 deletions spec/await_async_spec.cr
Expand Up @@ -61,5 +61,27 @@ module AwaitAsyncSpec
await f
await f
end

it "can await with timeout" do
x = async { sleep 0.5 }

expect_raises MiniFuture::TimeoutException do
await 0.2.seconds, x
end

x = async { sleep 0.1 }
await 0.2.seconds, x # < Should not raise exception
end
end

it "can await with timeout (array)" do
x = 5.times.map { |arr| async { sleep 0.5 } }.to_a

expect_raises MiniFuture::TimeoutException do
await 0.2.seconds, x
end

x = 5.times.map { async { sleep 0.1 } }.to_a
await 0.2.seconds, x # < Should not raise exception
end
end
37 changes: 32 additions & 5 deletions src/await_async.cr
@@ -1,5 +1,8 @@
# Lightweight Future structure.
class MiniFuture(T)
class TimeoutException < Exception
end

@status = :running
@channel = Channel(T).new(1)
@error : Exception? = nil
Expand Down Expand Up @@ -31,10 +34,26 @@ class MiniFuture(T)
@status == :running
end

def await
def await(timeout : Time::Span? = nil)
if @status != :flushed
@status = :flushed
@output = @channel.receive
if timeout
timeout_channel = Channel(Nil).new

spawn do
sleep timeout.not_nil!
timeout_channel.send nil unless @status == :flushed
end

select
when timeout_channel.receive
raise TimeoutException.new
when @output = @channel.receive
@status = :flushed
end
else
@status = :flushed
@output = @channel.receive
end
end

if e = @error
Expand All @@ -53,8 +72,16 @@ def await(future : MiniFuture)
future.await
end

def await(futures : Iterator(MiniFuture(T))) forall T
futures.map { |x| await x }
def await(timeout : Time::Span, future : MiniFuture)
future.await(timeout)
end

def await(futures : Iterator(MiniFuture(T)) | Array(MiniFuture(T))) forall T
futures.map(&.await)
end

def await(timeout : Time::Span, futures : Iterator(MiniFuture(T)) | Array(MiniFuture(T))) forall T
futures.map(&.await(timeout))
end

macro async(method)
Expand Down

0 comments on commit fe7e45c

Please sign in to comment.