Jakub Arnold
Jakub Arnold

Reputation: 87210

How can I handle a callback synchrnously in Node.js?

I'm using Redis to generate IDs for my in memory stored models. The Redis client requires a callback to the INCR command, which means the code looks like

client.incr('foo', function(err, id) {
  ... continue on here
});

The problem is, that I already have written the other part of the app, that expects the incr call to be synchronous and just return the ID, so that I can use it like

var id = client.incr('foo');

The reason why I got to this problem is that up until now, I was generating the IDs just in memory with a simple closure counter function, like

var counter = (function() {
  var count = 0;
  return function() {
    return ++count;
  }
})();

to simplify the testing and just general setup.

Does this mean that my app is flawed by design and I need to rewrite it to expect callback on generating IDs? Or is there any simple way to just synchronize the call?

Upvotes: 0

Views: 644

Answers (4)

snez
snez

Reputation: 2480

The application state in node.js is normally passed around as an object. What I would do is closer to:

var state = {}

client.incr('foo', function(err, id) {
  state.id = id;
  doSomethingWithId(state.id);
});

function doSomethingWithId(id) {
  // reuse state if necessary
}

It's just a different way of doing things.

Upvotes: 0

jdi
jdi

Reputation: 92559

@Sergio said this briefly in his answer, but I wanted to write a little more of an expanded answer. node.js is an asynchronous design. It runs in a single thread, which means that in order to remain fast and handle many concurrent operations, all blocking calls must have a callback for their return value to run them asynchronously.

That does not mean that synchronous calls are not possible. They are, and its a concern for how you trust 3rd party plugins. If someone decides to write a call in their plugin that does block, you are at the mercy of that call, where it might even be something that is internal and not exposed in their API. Thus, it can block your entire app. Consider what might happen if Redis took a significant amount of time to return, and then multiple that by the amount of clients that could potentially be accessing that same routine. The entire logic has been serialized and they all wait.

In answer to your last question, you should not work towards accommodating a blocking approach. It may seems like a simple solution now, but its counter-intuitive to the benefits of node.js in the first place. If you are only more comfortable in a synchronous design workflow, you may want to consider another framework that is designed that way (with threads). If you want to stick with node.js, rewrite your existing logic to conform to a callback style. From the code examples I have seen, it tends to look like a nested set of functions, as callback uses callback, etc, until it can return from that recursive stack.

Upvotes: 0

Femi
Femi

Reputation: 64690

It is a bit of a pain, but what you have to do is wrap the logic that you had after the counter was generated into a function, and call that from the Redis callback. If you had something like this:

var id = get_synchronous_id();
processIdSomehow(id);

you'll need to do something like this.

var runIdLogic = function(id){
  processIdSomehow(id);
}

client.incr('foo', function(err, id) {
  runIdLogic(id);
});

You'll need the appropriate error checking, but something like that should work for you.

There are a couple of sequential programming layers for Node (such as TameJS) that might help with what you want, but those generally do recompilation or things like that: you'll have to decide how comfortable you are with that if you want to use them.

Upvotes: 2

Sergio Tulentsev
Sergio Tulentsev

Reputation: 230286

Node.js in its essence is an async I/O library (with plugins). So, by definition, there's no synchronous I/O there and you should rewrite your app.

Upvotes: 3

Related Questions