Fcoder
Fcoder

Reputation: 9216

Running code sequentially in Node.js

I have a function that fetches data from a database:

recentItems = function () {
  Items.find({item_published: true}).exec(function(err,item){
      if(!err)
        return item
  });
};

And I want to use it like this:

var x = recentItems();

But this fails with undefined value due to Async behavior of recentItems. I know that I can change my function to use a callback like this:

recentItems = function (callback) {
  Items.find({item_published: true}).exec(function(err,item){
      if(!err)
        callback(item)
  });
};

And:

recentItems(function(result){
  var x = result;
});

But i dont want to use this method because i have a situation like this. i have a function that should do two operations and pus result to an array and after them, fire a callback and return value:

var calc = function(callback){
   var arr = [];

   var b = getValues();
   arr.push(b);

   recentItems(function(result){
     var x = result;
     arr.push(x);
   });

   callback(arr);
};

In this situation, the value of b pushed to arr and the main callback called and after that value of x fetched from recentItems duo to Async behavior of recentItems. But I need this two operation runs sequentially and one after one. After calculating all of them, then last line runs and the callback fired.

How can I resolve this? I read about the Promises and Async libraries, but I don't know which of them is my answer. Can I overcome this with raw Node.js? If so, I would prefer that.

Upvotes: 3

Views: 3874

Answers (3)

Buzinas
Buzinas

Reputation: 11725

There are some ways of doing what you want, but none of them are ~perfect~ yet.

There is an ES7 proposal of native async/await that will be the callback heaven, but atm, you can do:

  • Nested callbacks (native, but very ugly and unmaintainable code)
  • Promises (good, but still too verbose)
  • Async/Await library (It's an amazing library, but very far from native, and performance isn't cool)
  • ES7 transpiler - you can write the ES7 code today, and it will transpile for you to ES5 (e.g Babel)

But, if you're already using the newest version of NodeJS (4.0.0 as the time of writing) - and if you're not, you really should - the best way of achieving what you want is to use generators.

Combined with a small library named co, it will help you to achieve almost what the ES7 async/await proposes, and it will mostly use native code, so both readability and performance are really good:

var co = require('co');

var calc = co(function *calc() {
  var arr = [];
  var b = getValues();
  arr.push(b);

  var items = yield recentItems();
  arr.push(items);

  return arr;
});

function recentItems() {
  return new Promise(function(resolve) {
    Items.find({item_published: true}).exec(function(err, item) {
      if(!err)
        resolve(item);
  });
}

You can read more about this subject in this awesome Thomas Hunter's blog post.

Upvotes: 3

user4062487
user4062487

Reputation:

You can try something like this. It still nests the callbacks, but the code is a little cleaner.

var callA = function(callback) {
  //Run the first call
  prompt(callback(data));
}


var callB = function(callback) {
  //Some other call
  prompt(callback(data));
}

callA(function(dataA) {
  callB(function(dataB) {
    //Place a handler function here
    console.log(dataA + " " + dataB)
  })
});

Upvotes: 1

slebetman
slebetman

Reputation: 113878

You've almost got it. There is no method to work-around callbacks. However, you can certainly use callbacks to do what you want. Simply nest them:

var calc = function(callback){
   var arr = [];

   getValues(function(b){
       arr.push(b);

       recentItems(function(result){
         var x = result;
         arr.push(x);

         callback(arr);
       });
   });
};

Upvotes: 1

Related Questions