emmanueltouzery / prelude-ts

Functional programming, immutable collections and FP constructs for typescript and javascript
ISC License
377 stars 21 forks source link

Future Sequence / MaxConcurrent = 1 #25

Closed dkirchhof closed 5 years ago

dkirchhof commented 5 years ago

Hi, i'm trying to run some futures after each other, so i thought i could use the traverse function with maxConcurrent property set to one.

In my example, it doesn't work as i expected. The returned result shows me, that all futures are triggered at the same time.


const identity = x => x;

const createFuture = (message, timeout, ok) => 
    Future.ofPromiseCtor((resolve, reject) => {
        console.log("before:", message);

        setTimeout(() => { 
            console.log("after:", message); 
            return ok ? resolve("resolved") : reject("rejected")
        }, timeout);
    });

const futures = [
    createFuture("aaa", 1000, true),
    createFuture("bbb", 500, true),
];

Future.traverse(futures, identity, { maxConcurrent: 1 });

The log:

before: aaa
before: bbb
after: bbb
after: aaa
emmanueltouzery commented 5 years ago

Ok actually this is correct behaviour. In fact, if you take your example and comment the last line, which calls Future.traverse then you'll get the same output. The reason is that createFuture already creates the Future => already starts it (prelude-ts had lazy futures for a while, which didn't autostart, but we've had to give it up as it was pretty complicated to get right, and now have eager futures, like JS promises which are also eager).

That's why it's not possible to implement maxConcurrent with Future.sequence but only with Future.traverse. sequence takes a list of already instanciated (and therefore started) futures. Traverse on the other hand takes a list of something which can get converted to Futures. But by giving it futures directly, with a transform function of id, it cannot work.

This works as you expected:

const createFuture = (message, timeout, ok) => 
    Future.ofPromiseCtor((resolve, reject) => {
        console.log("before:", message);

        setTimeout(() => { 
            console.log("after:", message); 
            return ok ? resolve("resolved") : reject("rejected")
        }, timeout);
    });

Future.traverse([["aaa",1000], ["bbb", 500]],
         ([desc,delay]) => createFuture(desc, delay, true), { maxConcurrent: 1 });

I'm going to close this bug now, but please feel free to reopen it or to add further comments if you don't agree or something is not clear to you, and thank you for the report in any case!