Christopher Klewes
Christopher Klewes

Reputation: 11435

How to to nest a sequence with Q?

I would like to handle a sequence of tasks and get informed as soon as a block of those tasks is completed and once every task is completed. The output I expect should be 1,2,3 - 4,5,6 - 7 - 8. With my current implementation I get 1,4,7,8 - 2,5 - 3,6

function handleTasks(tasks) {
    var deferred = Q();
    var promises = []

    tasks.forEach(function (task) {
        promises.push(function () {
            return handle(task);
        });
    });

    promises.reduce(Q.when, new Q()).then(function () {
        // Finished inner hunk.
        deferred.resolve();
    });

    return deferred.promise;
}

function handle(t) {
    var deferred = Q.defer();
    document.write("started " + t);

    Q.delay(5000).then(function () {
        document.write("finished " + t);
        deferred.resolve();
    });

    return deferred.promise;
}

var deferred = Q();
var tasks = [[1, 2, 3], [4, 5, 6], [7], [8]];
var promises = []

tasks.forEach(function (task) {
    promises.push(function () {
        return handleTasks(task);
    });
});

promises.reduce(Q.when, new Q()).then(function () {
    // Finished all tasks
    deferred.resolve();
});
<script src="http://cdnjs.cloudflare.com/ajax/libs/q.js/0.9.6/q.js"></script>

Upvotes: 2

Views: 58

Answers (1)

JLRishe
JLRishe

Reputation: 101652

I'm really not sure how your code is producing any output at all. You're trying to use a promises as though they were deferreds, and you're using document.write(), which I would imagine is overwriting your whole page.

And while not necessarily a bug, you are using the deferred antipattern.

So I'm not really sure why you got the result you did, but here is a cleaner approach that produces the desired result:

function handleTasks(tasks) {
    var promiseFuncs = tasks.map(function (task) {
        return function () {
            return handle(task);
        };
    });

    return promiseFuncs.reduce(Q.when, new Q()).then(function () {
        console.log("finished " + JSON.stringify(tasks));
    });
}

function handle(t) {
   console.log("started " + t);

    return Q.delay(5000).then(function () {
        console.log("finished " + t);
        return t;
    });
}

var tasks = [[1, 2, 3], [4, 5, 6], [7], [8]];
var promiseFuncs = tasks.map(function (task) {
    return function () {
        return handleTasks(task);
    };
});

promiseFuncs.reduce(Q.when, new Q());
<script src="http://cdnjs.cloudflare.com/ajax/libs/q.js/0.9.6/q.js"></script>

You can also eliminate some of the duplication here with a helper function:

function runSequence(items, action) {
    return items.map(function (item) {
        return function () {
            return action(item);
        };
    }).reduce(Q.when, new Q());
}

function handleTasks(tasks) {
    return runSequence(tasks, handle).then(function () {
        console.log("finished " + JSON.stringify(tasks));
    });
}

function handle(t) {
   console.log("started " + t);

    return Q.delay(5000).then(function () {
        console.log("finished " + t);
        return t;
    });
}

var tasks = [[1, 2, 3], [4, 5, 6], [7], [8]];
runSequence(tasks, handleTasks);
<script src="http://cdnjs.cloudflare.com/ajax/libs/q.js/0.9.6/q.js"></script>

Upvotes: 2

Related Questions