Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to block a function until previous one is finished? nodejs

Tags:

node.js

have a for loop with a dynamic value that could be small to big, I want to make sure one call to search is finished before the next one can start. how do I do that? I've read about process.nextTick and setImmediate but I'm unsure how to use either in this context.

function search(x) {
      dns.resolve(x, function (err, addresses) {
           if (!err) {
                res.send("bad");
           } else {
                res.send("good");
           }
      });
}

for(a = 0; a < queries.length; a++) {
    query = queries[a];
    search(query);
}
like image 542
archytect Avatar asked Jul 29 '13 23:07

archytect


2 Answers

There are a few libraries out there that can help you organize the execution of asynchronous code. Async is the one I use and it's eachSeries() is useful here:

function search(x,callback) {
  dns.resolve(x, function (err, addresses) {
    if (!err) {
      res.send("bad");
    } else {
      res.send("good");
    }
    callback(err);
  });
}

async.eachSeries(queries,
  function(query,callback) {
    search(query,callback);
  },
  function(err) {
    if(err) {
      console.log("we had an error");
    }
  }
);

Note that Async will call the final callback as soon as one of the iterations has an error so if you don't want to stop there, you'll need to call callback() in the search() instead of callback(err).

UPDATE (without using a library):

If you don't want to use a library, you could implement it yourself like this:

function searchInternal(queries, idx, callback) {
  if(idx === queries.length) {
    callback();
    return;
  }

  dns.resolve(queries[idx], function (err, addresses) {
    if (!err) {
      res.send("bad");
    } else {
      res.send("good");
    }
    searchInternal(queries, idx+1, callback);
  });
}

function searchAll(queries, callback) {
  searchInternal(queries, 0, callback);
}

searchAll(queries, function() {
  console.log("all done now");
});

Note, this code isn't tested, and probably isn't the best implementation, but thats why we use libraries.

like image 132
go-oleg Avatar answered Sep 22 '22 14:09

go-oleg


I usually just use event emitters to make it all synchronous so I can still work within the asynchronous environment mindset. In my code below, whenever a DNS resolve gets finished, it produces an event that is listened to by the search function and lets it know to fire a new search. Plus, you get to learn how to create your own Event Emitters, which are awesome.

If you want to make it asynchronous for a certain size of the domain name array, you can create a denominator variable and use the modulus operator to send asynchronous in chunks and only fire a synchronous event(to clear the async buffer) whenever the modulus reaches 0.

// program that uses event emitters to create sync code in an async env

var dns = require('dns')                                //dns from core
var eventEmitter = require('events').EventEmitter       //Event Emitter from core

var ee = new eventEmitter;      //make an Event Emitter object

var queries = ['yahoo.com','google.com','james.com'];

ee.on('next', next_search);     //create a listener for an event we define

// our listening function that executes on our defined 'next' event
function next_search() {
        search(queries[a]);
        if(queries.length == a) process.exit(0);
        ++a;
}

// the actual search function that uses DNS
function search(x) {
        dns.resolve(x, function (err) {
                if (!err) {
                        //res.send("bad");
                        console.log('bad: ' + x)
                        ee.emit('next')
                } else {
                        //res.send("good");
                        console.log('good: ' + x)
                        ee.emit('next')
                }
        });
}


// global variable to keep track of our name queue length
var a = 0;

// kick it all off
next_search()
like image 42
EhevuTov Avatar answered Sep 23 '22 14:09

EhevuTov