Re: [elixir-core:10193] Proposal: Add function to return the first async task to complete

2021-04-02 Thread José Valim
PRs to improve the docs is always welcome!

On Fri, Apr 2, 2021 at 21:02 thia.md...@gmail.com 
wrote:

> > And this made me wonder: is there any reason why you can't use
> Task.async_stream?
>
> After testing, I'm sure we can accomplish the same thing that was proposed
> with Task.async_stream. There is really no reason for an extra function.
>
> One thing that would be nice is to have an example for this usage in the
> documentation. I honestly never thought in using Task.async_stream in this
> way.
>
> WDYT? PRs for adding this example to the documentation would be welcome?
>
> Em sexta-feira, 2 de abril de 2021 às 08:39:18 UTC-3, emt...@gmail.com
> escreveu:
>
>> Funny that exactly on this day that the proposal was opened I said in two
>> groups the lack that I was doing something similar to "Promise.any" in
>> Elixir.
>>
>> I needed it and found it complicated to do now, I had a job because I'm
>> starting in Elixir.
>>
>> I would love to have another solution for what I did, when researching
>> several APIs to return the CEP query to me and get the first valid result.
>>
>> I'm starting in Elixir and in love, and I made my first package with
>> exactly this need:
>> https://github.com/ciareis/cep-promise-elixir/blob/master/lib/cep_promise.ex#L52
>>
>> I could probably write everything in a better way, but I still get there.
>>
>> Please implement something to facilitate this;)
>>
>> Elixir S2!
>> Em sexta-feira, 2 de abril de 2021 às 04:51:53 UTC-3, José Valim escreveu:
>>
>>> I have been thinking more about this problem and there is still one
>>> issue for us to solve: when you say you are waiting for "n" tasks, does it
>>> mean you are waiting for "n" successes or "n" results (regardless if they
>>> are success or failure)?
>>>
>>> So I thought perhaps a better solution is to introduce something called
>>> Task.yield_stream/2. "yield_stream" receives an enumerable of tasks and
>>> emits them as they complete. To get the first successful task you would do:
>>>
>>> tasks
>>> |> Task.yield_stream(ordered: false, timeout: 5000)
>>> |> Stream.filter(?({:ok, _}))
>>> |> Enum.at(0)
>>>
>>> And this made me wonder: is there any reason why you can't use
>>> Task.async_stream?
>>>
>>>
>>> On Thu, Apr 1, 2021 at 3:59 PM José Valim  wrote:
>>>
 I don't see a reason why we wouldn't return the same order. You can
 easily get the ones you want by `for {task, {:ok, value}} <- result do`.
 Plus forcing people to iterate will remind them that they most likely need
 to shutdown the other tasks.

 On Thu, Apr 1, 2021 at 3:57 PM thia.md...@gmail.com <
 thia.md...@gmail.com> wrote:

> > In this sense, yield_many becomes a special case of yield_first
> where you want to yield on all given tasks.
>
> I'm not sure if we would want that, because in yield_many the returned
> list will be in the same order as the tasks supplied in the tasks
> input argument.
> However yield_first to preserve the semantics of "returning as soon it
> finishes", maybe we should return the tasks in the order of finish (the
> first to complete first in the list).
> That makes sense?
>
> Em quarta-feira, 31 de março de 2021 às 19:35:47 UTC-3,
> thia.md...@gmail.com escreveu:
>
>> +1 for yield_first(tasks, n, timeout)
>>
>> It seems to better convey the meaning "yield the first n tasks".
>>
>> Em qua., 31 de mar. de 2021 às 19:26, Felipe Stival 
>> escreveu:
>>
>>> +1 for yield_first(tasks, n, timeout)
>>>
>>>
>>> On Thu, Apr 1, 2021, 01:11 José Valim  wrote:
>>>
 It was not possible to implement yield_first in Elixir but now that
 we have map_get in guards, we can easily do so by putting all refs in 
 a map
 and only getting messages from the inbox where the ref is in the map. 
 The
 number of tasks to wait and the maximum timeout should be configurable 
 too.
 For example:

 yield_first(task, 3, 1000)

 The above will yield the first 3 tasks within 1000ms. It should
 have the same result type as yield_many. In this sense, yield_many 
 becomes
 a special case of yield_first where you want to yield on all given 
 tasks.

 Another option is to not introduce a new function but instead
 introduce a new argument to yield_many with the limit to yield:

 yield_many(task, 1000, 3)



 On Wed, Mar 31, 2021 at 11:52 PM thia.md...@gmail.com <
 thia.md...@gmail.com> wrote:

> I think the proposal would work differently from yield_many.
> yield_many "receives a list of tasks and waits for their replies in 
> the
> given time interval".
> The proposal of the new function is to return as soon as the first
> task finishes.
>
> For 

Re: [elixir-core:10193] Proposal: Add function to return the first async task to complete

2021-04-02 Thread thia.md...@gmail.com
> And this made me wonder: is there any reason why you can't use 
Task.async_stream?

After testing, I'm sure we can accomplish the same thing that was proposed 
with Task.async_stream. There is really no reason for an extra function.

One thing that would be nice is to have an example for this usage in the 
documentation. I honestly never thought in using Task.async_stream in this 
way. 

WDYT? PRs for adding this example to the documentation would be welcome?

Em sexta-feira, 2 de abril de 2021 às 08:39:18 UTC-3, emt...@gmail.com 
escreveu:

> Funny that exactly on this day that the proposal was opened I said in two 
> groups the lack that I was doing something similar to "Promise.any" in 
> Elixir.
>
> I needed it and found it complicated to do now, I had a job because I'm 
> starting in Elixir.
>
> I would love to have another solution for what I did, when researching 
> several APIs to return the CEP query to me and get the first valid result.
>
> I'm starting in Elixir and in love, and I made my first package with 
> exactly this need: 
> https://github.com/ciareis/cep-promise-elixir/blob/master/lib/cep_promise.ex#L52
>
> I could probably write everything in a better way, but I still get there.
>
> Please implement something to facilitate this;)
>
> Elixir S2!
> Em sexta-feira, 2 de abril de 2021 às 04:51:53 UTC-3, José Valim escreveu:
>
>> I have been thinking more about this problem and there is still one issue 
>> for us to solve: when you say you are waiting for "n" tasks, does it mean 
>> you are waiting for "n" successes or "n" results (regardless if they are 
>> success or failure)?
>>
>> So I thought perhaps a better solution is to introduce something called 
>> Task.yield_stream/2. "yield_stream" receives an enumerable of tasks and 
>> emits them as they complete. To get the first successful task you would do:
>>
>> tasks
>> |> Task.yield_stream(ordered: false, timeout: 5000)
>> |> Stream.filter(?({:ok, _}))
>> |> Enum.at(0)
>>
>> And this made me wonder: is there any reason why you can't use 
>> Task.async_stream?
>>
>>
>> On Thu, Apr 1, 2021 at 3:59 PM José Valim  wrote:
>>
>>> I don't see a reason why we wouldn't return the same order. You can 
>>> easily get the ones you want by `for {task, {:ok, value}} <- result do`. 
>>> Plus forcing people to iterate will remind them that they most likely need 
>>> to shutdown the other tasks.
>>>
>>> On Thu, Apr 1, 2021 at 3:57 PM thia.md...@gmail.com <
>>> thia.md...@gmail.com> wrote:
>>>
 > In this sense, yield_many becomes a special case of yield_first where 
 you want to yield on all given tasks.

 I'm not sure if we would want that, because in yield_many the returned 
 list will be in the same order as the tasks supplied in the tasks 
 input argument. 
 However yield_first to preserve the semantics of "returning as soon it 
 finishes", maybe we should return the tasks in the order of finish (the 
 first to complete first in the list). 
 That makes sense?

 Em quarta-feira, 31 de março de 2021 às 19:35:47 UTC-3, 
 thia.md...@gmail.com escreveu:

> +1 for yield_first(tasks, n, timeout)
>
> It seems to better convey the meaning "yield the first n tasks".
>
> Em qua., 31 de mar. de 2021 às 19:26, Felipe Stival  
> escreveu:
>
>> +1 for yield_first(tasks, n, timeout)
>>
>>
>> On Thu, Apr 1, 2021, 01:11 José Valim  wrote:
>>
>>> It was not possible to implement yield_first in Elixir but now that 
>>> we have map_get in guards, we can easily do so by putting all refs in a 
>>> map 
>>> and only getting messages from the inbox where the ref is in the map. 
>>> The 
>>> number of tasks to wait and the maximum timeout should be configurable 
>>> too. 
>>> For example:
>>>
>>> yield_first(task, 3, 1000)
>>>
>>> The above will yield the first 3 tasks within 1000ms. It should have 
>>> the same result type as yield_many. In this sense, yield_many becomes a 
>>> special case of yield_first where you want to yield on all given tasks.
>>>
>>> Another option is to not introduce a new function but instead 
>>> introduce a new argument to yield_many with the limit to yield:
>>>
>>> yield_many(task, 1000, 3)
>>>
>>>
>>>
>>> On Wed, Mar 31, 2021 at 11:52 PM thia.md...@gmail.com <
>>> thia.md...@gmail.com> wrote:
>>>
 I think the proposal would work differently from yield_many. 
 yield_many "receives a list of tasks and waits for their replies in 
 the 
 given time interval". 
 The proposal of the new function is to return as soon as the first 
 task finishes.

 For example, if we start tasks to make call to 3 different remote 
 APIs, with different response times.

 API A - 50ms
 API B - 500ms
 API C - 1500ms

 tasks = [