Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Q.allSeries()? #249

Closed
curvedmark opened this Issue · 5 comments

4 participants

@curvedmark

The Sequences example in README.md is quite nice, but only if the promise-generating functions depend one after another.

If these functions are independent of each other, then running them sequentially and get their fulfillment values quickly become awkward.

For example, imagine you need to test a server's network speed, and you need to test it repeatedly and sequentially to get an array of speed values in order to get an average. In the following examples, testspeed(url) returns a promise whose fulfillment value is the server network speed:

Version 1

repeat = 5;

var tests = Q([]);
for (var i = 0; i < repeat; ++i) {
    tests = tests.then(function (speeds) {
        speeds.push(testspeed(url));
        return Q.all(speeds);
    });
}
tests.then(function (speeds) {
    console.log(speeds);
});

This forces Q to constantly check array values when each promise is resolved. Not ideal.

Version 2

repeat = 5;

var tests = [];
for (var i = 0; i < repeat; ++i) {
    tests.push(testspeed(url));
}
tests.reduce(function (prevTests, test) {
    return prevTests.then(function (speeds) {
        return test.then(function (speed) {
            speeds.push(speed);
            return speeds;
        });
    });
}, Q([])).then(function (speeds) {
    console.log(speeds);
});

The Pyramid of Doom says it all.

Another Solution

I'm wondering if it's feasible to introduce a Q.allSeries function which will simplify the previous code to:

repeat = 5;

var tests = [];
for (var i = 0; i < repeat; ++i) {
    tests.push(testspeed(url));
}
Q.allSeries(tests).then(function (speeds) {
    console.log(speeds);
});

(BTW, is there a way to do the equivalent of Array.new(3, obj) in ruby quickly in js?)

Q has been around for awhile, but nobody seems to need such function (at least I couldn't find a related issue). Did I miss something or did I do something wrong? Or such use case is actually pretty rare?

@curvedmark

Actually, the real need is to be able to specify how many promises are allowed to run in parallel. Maybe .all() could receive one more argument?

@rkatic

Your first version can be changed to avoid the useage of Q.all:

repeat = 5;
speeds = [];

var done = Q();
for (var i = 0; i < repeat; ++i) {
    done = done.then(function (speed) {
        if (speed !== undefined) {
            speeds.push(speed);
        }
        return testspeed(url);
    });
}
done.then(function () {
    console.log(speeds);
});

Your second version is running testspeed in parallel.
A promise is only a holder for a wanted value, and it doesn't have control on how or when it is fulfilled.

@curvedmark

Ah, your version is much better, thanks.

My understanding of promises was completely wrong in the second version, but what I actually mean is this:

repeat = 5;

var tests = [];
for (var i = 0; i < repeat; ++i) {
    tests.push(testspeed.bind(undefined, url));
}

Q.series(tests).then(function (speeds) {
    console.log(speeds);
});

When given an array of promise-generating functions, Q.serial(array) should fulfill promises serially. Q.serial is actually a specialized version of Q.parallel(array, count), which should fulfill at most count promises in parallel.

This could potentially be useful, for example, when testing multiple servers, or requesting multiple files with xhr.

Here is my initial stab at it:

Q.parallel = function (funcs, count) {
    var length = funcs.length;
    if (!length) {
        return Q([]);
    }

    if (count == null) {
        count = Infinity;
    }

    count = Math.max(count, 1);
    count = Math.min(count, funcs.length);

    var promises = [];
    var values = [];
    for (var i = 0; i < count; ++i) {
        var promise = funcs[i]();
        promise = promise.then(next(i));
        promises.push(promise);
    }

    return Q.all(promises).then(function () {
        return values;
    });

    function next(i) {
        return function (value) {
            if (i == null) {
                i = count++;
            }

            if (i < length) {
                values[i] = value;
            }

            if (count < length) {
                return funcs[count]().then(next())
            }
        }
    }
};
@domenic
Collaborator

I think Q.parallel sounds pretty cool, but I'm not sure it's a common enough use case that we want to add it to the base Q library. @kriskowal, any thoughts in that direction?

Even if we don't add it, it might be a great npm package just by itself!

@kriskowal
Owner

Might run it by the Qx project.

@domenic domenic closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.