5

I'm new to Node and JavaScript (well, asynchronous programming in general) and I noticed when I was working on a project that the following code is a circular pattern and that these are bad practice for the obvious reason that the module might not have loaded yet (and the example code is throwing errors because of that).

Here's my code:

Main module

var module2 = require('./module2');

var data = 'data';

module2.fetchStuff(data);

Module2

var module3 = require('./module3');

var cleanDataArray = [];

function fetchStuff(data){
    // Fetches stuff based on data
    module3.cleanStuff(data);
}

function takeStuffBack(data){
    cleanData.push(data);
}

module.exports = {
    fetchStuff: fetchStuff,
    takeStuffBack: takeStuffBack,
    cleanData: cleanDataArray
};

Module3

var module2 = require('./module2');

function cleanStuff(data){
    // Clean data from needless stuff
    module2.takeStuffBack(data); // I get a TypeError here because `module2` is yet to fully load.
}

module.exports = {
    cleanStuff: cleanStuff
};


The XY

What this structure is supposed to do is for the start module to call a fetching function in module2, the fetching function needs to "wash" the data in the 3rd module before taking it back and providing it for whatever wants to export it. So I suppose the XY is that I need to do is to get data from a 3rd party API and then "clean" the data from the things it contains but I don't want, and then I need to make that "clean" version available to the rest of the application.

What other ways are there to do this in a better manner, without a circular pattern such as this which is broken because module2 won't load before module3 tries to call it?

  • The simple (X) fix is to just return the cleaned data and call doSomethingElse from module2 – ratchet freak Aug 3 '15 at 11:43
  • Oops, doSomethingElse should be takeStuffBack, edited! The fetchStuff is containing asynch code so a return wouldn't work – Gemtastic Aug 3 '15 at 11:49
  • The simplest way to avoid circular patterns is to avoid circular patterns. – Dave Nay Oct 2 '15 at 13:11
  • 3
    @DaveNay The simplest way to show someone how to do it is to show someone how to do it. – rwong Dec 1 '15 at 16:47
1

Using callbacks is the easier concept to implement from scratch for small scenarios. For long-term projects, usually Promises are implemented.

Bluebird is one library that gives them to you. They've also been added to the JavaScript specification, so maybe now they're accessible in Node? (Sorry; my main proficiency is clientside JS). To start with, see if your actual asynchronous library can return promises. If not, it shouldn't be difficult to wrap it with one. They generally look something like this.

function PromiseWrapFunction() {
  return new Promise(success, fail) {
    libMethod.doThing({
      onSuccess: success,
      error: fail
    });
  });
}

Then, most of your functions will look like this. Assuming that you don't expect the "onReturn" logic to be reusable, you can simply use an anonymous internal function rather than naming it.

function myAsyncFunction() {
  return myLibWrapper.PromiseWrapFunction().then(function(data) {
    var slightlyTransformedVersionOfData = {
      myVar: data.oldVar + 2
    };
    return slightlyTransformedVersionOfData;
  });
}

The basic idea here is that you can't return the final data yet, but you can return an object that represents the final data, and easily lets any accessor register callbacks (using .then(). The return value of that .then will be another such representative object, but its .then will return the modified data from your callback. Promises are a broad concept and this is a very quick summary, so try finding tutorials on them; they're usually a great way of avoiding circular patterns like these.

0

Use a callback, pass takeStuffBack to cleanStuff as callback taking the data:

function cleanStuff(data, continuation){
    // Clean data from needless stuff
    continuation(data); 
}

fetchStuff in module2 becomes:

function fetchStuff(data){
    // Fetches stuff based on data
    module3.cleanStuff(data, takeStuffBack);
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.