3

I trying to use _.find() in the context of a Q promise.

My problem seems to be from the fact that my predicate function contains a call to an asynchronous (node-style) function.

Here is my code:

var IsAlreadyQueued = function() {
    return Q.ninvoke(kueSearcher, 'delayed')
        .then(function(ids) {
            return Q(_.find(ids, function(id) {
                // return true; // This works
                kue.Job.get(id, function(err, job) {
                    if (job.type === 'jobtype') {
                        return true;
                    }
                });

            }));
        });
};

So as I said in comments in the code, if i do return true; instead of calling kue.Job.get() it works.

Can you see what's wrong with my use of _.find() in a promise?

Thanks a lot for your help.

Dominic Jodoin
  • 2,538
  • 18
  • 21

2 Answers2

2

No, you cannot use _.find or any other synchronous iteration method with asynchronous callbacks - it doesn't work with filter either. You currently try to return from an asynchronous callback, which just won't work.

You'll first need to write an asynchronous find function:

function find(arr, predicate, i) {
    i = i >>> 0;
    if (i >= arr.length)
        return Q.resolve(null);
    return predicate(arr[i], i).then(function(m) {
        return m ? arr[i] : find(arr, predicate, i+1);
    });
}

then you can rewrite your own method:

function isAlreadyQueued() {
    return Q.ninvoke(kueSearcher, 'delayed')
    .then(function(ids) {
        return find(ids, function(id) {
            return Q.npost(kue.Job, "get", id)
            .then(function(job) { return job.type === 'jobtype'; });
        });
    });
}
Community
  • 1
  • 1
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • Thank you for your answer. Somehow it looks like I made it work with my solution here: http://stackoverflow.com/a/28729693/213272. Can you have a look? Thanks a lot! – Dominic Jodoin Feb 25 '15 at 21:13
  • you dont need to define new functions, check out the lodash example in bluebird .call documentation, i believe same is possible in q – Esailija Feb 26 '15 at 19:18
  • @Esailija: You mean that `.call("map", function(fileNames, firstCh) {return Promise.props({…}); }).call("value").all().then(_)`? Whoa, that's disconcerting. And I can assure you that `_.find` does not know how to treat promise-returning callbacks, and there's no magic that can make it do! – Bergi Feb 26 '15 at 19:27
1

Somehow I made it work with this:

var isAlreadyQueued = function() {
    return Q.ninvoke(kueSearcher, 'delayed')
        .then(function(ids) {
            return Q(_.find(ids, function(id) {
                return Q.ninvoke(kue.Job, 'get', id)
                    .then(function(err, job) {
                        return (job.type === 'jobtype');
                    });
            })).then(function(id) {
                return !!id;
            });
        });
};

Can anybody verify my solution above? Thanks!

Dominic Jodoin
  • 2,538
  • 18
  • 21
  • No, that doesn't work, as `_.find` does not expect (and wait for) a promise as the return value of the predicate callback. It just sees it as a truthy value, and will yield you the first `id` in the array. – Bergi Feb 25 '15 at 21:30
  • My test was indeed wrong. Thanks a lot for your answer and help! – Dominic Jodoin Feb 26 '15 at 14:23