1

What I want to do:

var found = false;
while (!found){
  var result = db.getNextRecord();
  if (result == search_term){
    return result;
  }
}

The problem is, getNextRecord is asynchronous

var nothing_returned = db.getNextRecord(function(err, result){
  // I have the result in this callback, but not before
});

Given the behavior of getNextRecord(cb), how can I rewrite the above code snippet to get the same outcome?

Dave
  • 2,735
  • 8
  • 40
  • 44
  • FYI this is a perfect use case for generators, where you could do "yield db.getNextRecord();" and the loop would pause until the asynchronous call resolves. Generators are coming to javascript in ECMA 7 (you can use them now with a transpiler like babel) and are already supported in node.js – Charlie Martin Oct 09 '15 at 18:19

3 Answers3

1

Since you have a function that's async and you want to call in synchronously, you have two choice. Use a sync version of the method if there is one available, but if not, then you'll have to change your logic.

The following snippet should do what you want, it does require the async library.

var async = require('async');
var result;

async.whilst(
    function () {
        return !result;
    },
    function (callback) {
        db.getNextRecord(function (err, record) {
            if (err)
            {
                return callback(err);
            }

            if (result == search_term)
            {
                result = record;
            }

            return callback();
        });
    },
    function (err) {

        // Search is complete, do what you wish with result in this function. This function 
        // will be called when whilst is done or if getNextRecord got an error.

    }
);

I'm sure there's a shorter way to do this if you want to change the logic even more, but this is similar to doing a while but asynchronously.

jValdron
  • 3,408
  • 1
  • 28
  • 44
  • I'm confused a bit since you're using record and result. Where does result get defined when it's first used in the if statement near the middle of your code? – Dave Oct 09 '15 at 18:58
1

Use the async library. Its until function looks like what you need: https://www.npmjs.com/package/async#until

var async = require('async');

var latestResult = null;

async.until(function () {
  return latestResult == search_term;
}, function () {
  db.getNextRecord(function (err, result) {
    latestResult = result;
  });
}, function () {
  // now you can do something with latestResult
});

You should also consider whether it makes sense to do this in your app or have the database query include this filtering.

Brandon
  • 9,822
  • 3
  • 27
  • 37
  • What variables does the test function have access to? I'm assuming search_term is defined above, but is it safe to assume that whatever's set in the callback to getNextRecord is available to test as well? – Dave Oct 09 '15 at 18:56
  • That's what I was trying to figure out earlier. It'd be nice if getNextRecord could pass a variable to the test function, but I didn't see any way to do it, thus the closure in the outer scope. – Brandon Oct 09 '15 at 19:47
0

With babel and new JS:

import {promisify as pr} from 'es6-promisify';

async function find(search_term) {
  let found = false, result=null;
  while (!found){
    let result = await pr(db.getNextRecord)();
    if (result == search_term){
      found=true;
    }
  }
  return result;
}
Jason Livesay
  • 6,317
  • 3
  • 25
  • 31
  • How does await know that the result is the second parameter to getNextRecord and not the first? – Dave Oct 09 '15 at 18:54
  • Promisify converts the standard Node callback with an error first to a promise and async/await works with promises. Research 'es7 async' 'javascript promises' 'babel'. It will take some time and practice to learn that stuff. – Jason Livesay Oct 09 '15 at 19:05