Deferred.notify() inside async.whilst() doesn't trigger progress handler until callback

174 Views Asked by At

I have a function that returns a promise (using Q), and the notifications don't seem to happen at the right time. The onFulfilled and onRejected callback work as intended, but the progress callback doesn't fire until after async.whilst() finishes running, and fires everything at once.

This is the function

function generateSentences(data, num, options) {
    var deferred = Q.defer();
    const markov = new Markov(data, options);
    markov.buildCorpus()
        .then(() => {
            var count = 0;
            async.whilst(
                function () { return count < num; },
                function (callback) {
                    markov.generateSentence()
                        .then(result => {
                            console.log("Count: " + count);
                            deferred.notify(count / num); //update progress
                            count++;
                            callback(null);
                        }, (err) => {
                            deferred.reject(err.toString());
                            count++;
                        });
                },
                function (err, n) {
                    //PROGRESS EVENTS DON'T HAPPEN UNTIL HERE
                    deferred.resolve(generatedSentences); //finish
                }
            );
        }, (err) => console.log(err));
    return deferred.promise;
}

and this is using the promise

function generateScript() {
    fs.readdir(parser.videoBasePath, function (err, files) {
        parseFiles(files, parser.parse).then((a) => {
            console.log("Total Lines: " + fullScript.length + "\n");
            fullScript = _.shuffle(fullScript);
            markov.generateSentences(fullScript, 20).then((data) => {
                console.log(data);
            }, (err) => {
                console.log(err);
            }, (progress) => {
                console.log(progress);
            });
        });
    });
}

I've read some threads like this saying I need to wrap a setTimeout around the notify(), but it doesn't seem to affect anything.

1

There are 1 best solutions below

4
On

I've read that Promises + async.js don't mix (but can't find anything to say as much!!), and I can't really see why that should be an issue in this case to be honest

Having said that, the code you've shown seems to be possible without async, so try this to see if the progress works any better

function generateSentences(data, num, options) {
    var deferred = Q.defer();
    const markov = new Markov(data, options);
    const genSentence = count => markov.generateSentence()
        .then(result => {
            console.log("Count: " + count);
            deferred.notify(count / num); //update progress
            if (count < num) {
                return genSentence(count + 1);
            }
        });
    markov.buildCorpus()
        .then(() => genSentence(0))
        .then(() => deferred.resolve(generatedSentences)) //finish
        .catch(err => deferred.reject(err.toString()));
    return deferred.promise;
}

At the very least, the code is (in my opinion) a little cleaner anyway