How can I specify the order in which node.js functions are evaluated?
Asked Answered
P

3

1

Below, I tried to evaluate the im.identify before console.log(toObtain), but it appears that console.log(toObtain) was called after im.identify. How can I make sure that the functions are called in exactly the order that I want them to be called?

var toObtain; //I'm trying to set the value of this variable to features (inside the callback).

var im = require('imagemagick');
im.identify('kittens.png', function(err, features){
  if (err) throw err
  //console.log(features);
  toObtain = features;
  console.log(toObtain); //this prints { format: 'PNG', width: 400, height: 300, depth: 8 }
})

console.log(toObtain); //This function call is evaluated BEFORE im.identify, which is exactly the opposite of what I wanted to do.
Patrizius answered 22/9, 2012 at 3:22 Comment(2)
It appears that there is a duplicate of this question: #9122402Patrizius
And here's another duplicate: #5951850Patrizius
A
2

Asynchronous means that things occur when they are ready (without any synchronous relationship to each other.)

This implies that if you wish to print or otherwise operate on a value created within an asynchronous function, you MUST do so in its callback since that is the only place that the value is guaranteed to be available.

If you wish to order multiple activities so they occur one after another, you'll need to apply one of the various techniques which "chain" asynchronous functions in a synchronous way (one after another) such as the async javascript library written by caolan.

Using async in your example, you would:

var async=require('async');

var toObtain;

async.series([

  function(next){ // step one - call the function that sets toObtain

    im.identify('kittens.png', function(err, features){
      if (err) throw err;
      toObtain = features;
      next(); // invoke the callback provided by async
    });

  },

  function(next){ // step two - display it
    console.log('the value of toObtain is: %s',toObtain.toString());
  }
]);

The reason this works is because the async library provides a special callback that is used to move to the next function in the series which must be called when the current action has completed.

See the async documentation for further info, including how to pass values through the next() callback to the next function in the series and as the result of the async.series() function.

async can be installed to your application using:

npm install async

or globally, so it is available to all your nodejs applications, using:

npm install -g async
Agle answered 22/9, 2012 at 3:52 Comment(13)
Although async module is popular library I think you will agree that its better to do this without other a library when you are still learning. By all means use it later if it has significant advantages.Cirrocumulus
@Cirrocumulus I'm afraid I can't agree with you here. Wrapping one's head around asynchronous "event-driven" programming is hard because we all tend to think in terms of causal relationships, where one thing "causes" another, and event-driven programming tends to obscure the causal relationships between programming actions.Agle
Having said that, if you're interested in "rolling your own" library to run asynchronous functions in order, you might check out "runner" in my github sandbox. (github.com/raisch/sandbox/tree/master/runner)Agle
@RobRaisch Is there any documentation for this library? I'd like to know how I can use this library to evaluate synchronous functions in a specific order, but I couldn't find a "readme.md" or "readme.txt".Patrizius
@RobRaisch I'm getting the following error in the terminal when I try to run the example above: ReferenceError: im is not definedPatrizius
@RobRaisch Also, is it possible to make async.series return a value (in this case, the value of toObtain)? I'm still trying to find a way to make toObtain accessible outside of a callback. (If async.series can't do this, is there another function for this purpose)?Patrizius
@RobRaisch When I add this function to async.series, I notice that it has no effect at all: function(next){ alert(toObtain.toString()); } How should this be modified so that it will work properly?Patrizius
@AndersonGreen I provided a link to the async library documentation above. There is no doc for my runner as it was an experiment to see how easy it was to create my own version of async's series function. (github.com/caolan/async)Agle
@AndersonGreen The reference to the 'im' object in my answer was taken from your own code example.Agle
@AndersonGreen The alert() function isn't very useful under nodejs, which is a server and as such, has no window or browser it might display an alert on. That is why my example used console.log().Agle
@AndersonGreen Yes, async.series() can return a value. Please see its documentation for further information on this subject.Agle
@RobRaisch Where can the documentation for async.series be found?Patrizius
@RobRaisch Also, I searched through the documentation and I couldn't find how to make async.series return a value.Patrizius
C
2

This is how node works. This is normal async behaviour.

To save yourself from going nuts when working with node its important to break things up to functions that each have specific purpose. Each function will call the next passing parameters as needed.

var im = require('imagemagick');

var identify = function () {

      im.identify('kittens.png', function(err, features){

         doSomething(features) // call doSomething function passing features a parameter.

      })

}(); // this is the first function so it should self-execute.

var doSomething = function (features) {

       var toObtain = features;

       console.log(toObtain) //or whatever you want to do with toObtain variable
};
Cirrocumulus answered 22/9, 2012 at 3:50 Comment(1)
Still, is it possible to cause asynchrous functions to execute in a specific order?Patrizius
A
2

Asynchronous means that things occur when they are ready (without any synchronous relationship to each other.)

This implies that if you wish to print or otherwise operate on a value created within an asynchronous function, you MUST do so in its callback since that is the only place that the value is guaranteed to be available.

If you wish to order multiple activities so they occur one after another, you'll need to apply one of the various techniques which "chain" asynchronous functions in a synchronous way (one after another) such as the async javascript library written by caolan.

Using async in your example, you would:

var async=require('async');

var toObtain;

async.series([

  function(next){ // step one - call the function that sets toObtain

    im.identify('kittens.png', function(err, features){
      if (err) throw err;
      toObtain = features;
      next(); // invoke the callback provided by async
    });

  },

  function(next){ // step two - display it
    console.log('the value of toObtain is: %s',toObtain.toString());
  }
]);

The reason this works is because the async library provides a special callback that is used to move to the next function in the series which must be called when the current action has completed.

See the async documentation for further info, including how to pass values through the next() callback to the next function in the series and as the result of the async.series() function.

async can be installed to your application using:

npm install async

or globally, so it is available to all your nodejs applications, using:

npm install -g async
Agle answered 22/9, 2012 at 3:52 Comment(13)
Although async module is popular library I think you will agree that its better to do this without other a library when you are still learning. By all means use it later if it has significant advantages.Cirrocumulus
@Cirrocumulus I'm afraid I can't agree with you here. Wrapping one's head around asynchronous "event-driven" programming is hard because we all tend to think in terms of causal relationships, where one thing "causes" another, and event-driven programming tends to obscure the causal relationships between programming actions.Agle
Having said that, if you're interested in "rolling your own" library to run asynchronous functions in order, you might check out "runner" in my github sandbox. (github.com/raisch/sandbox/tree/master/runner)Agle
@RobRaisch Is there any documentation for this library? I'd like to know how I can use this library to evaluate synchronous functions in a specific order, but I couldn't find a "readme.md" or "readme.txt".Patrizius
@RobRaisch I'm getting the following error in the terminal when I try to run the example above: ReferenceError: im is not definedPatrizius
@RobRaisch Also, is it possible to make async.series return a value (in this case, the value of toObtain)? I'm still trying to find a way to make toObtain accessible outside of a callback. (If async.series can't do this, is there another function for this purpose)?Patrizius
@RobRaisch When I add this function to async.series, I notice that it has no effect at all: function(next){ alert(toObtain.toString()); } How should this be modified so that it will work properly?Patrizius
@AndersonGreen I provided a link to the async library documentation above. There is no doc for my runner as it was an experiment to see how easy it was to create my own version of async's series function. (github.com/caolan/async)Agle
@AndersonGreen The reference to the 'im' object in my answer was taken from your own code example.Agle
@AndersonGreen The alert() function isn't very useful under nodejs, which is a server and as such, has no window or browser it might display an alert on. That is why my example used console.log().Agle
@AndersonGreen Yes, async.series() can return a value. Please see its documentation for further information on this subject.Agle
@RobRaisch Where can the documentation for async.series be found?Patrizius
@RobRaisch Also, I searched through the documentation and I couldn't find how to make async.series return a value.Patrizius
V
0

This is normal, since im.identify seams to be async (it requires a callback). Therefore, the callback can get executed after the next line.

EDIT : You therefore have to put any code that users toObtain IN your callback, after toObtain = features;

Volvox answered 22/9, 2012 at 3:24 Comment(3)
Is it possible to get the value of toObtain outside the callback? I'm trying write a function that actually returns the value of toObtain so that it can be used outside the callback.Patrizius
Is it possible to make the callback be evaluated before the last line in the script (which is outside of the callback)?Patrizius
No, that is the very nature of async (in this case, node.js). You have to change the way you think. In this case, if you'd have some things to do with the result (features), and if you want to encapsulate the behaviour, you could create a function that takes a function as a parameter, and executes it in the im.identify callback, passing it the data you want.Volvox

© 2022 - 2024 — McMap. All rights reserved.