Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to avoid circular dependency between modules when working with recursive functions?

The following code traverses an object and its keys. It does a thing for each key depending on what kind of value it refers to. It works well. All is good.

const obj = {
  prop1: {
    prop2: 1,
    prop3: {
      prop4: 2
    }
  },
  prop5: "1"
}

function inspectRecursively(node) {
  switch (typeof node) {
    case "object":
      handleObject(node);
      break;
    case "string":
      handleString(node);
      break;
    case "number":
      handleNumber(node);
      break;
    default:
      break;
  }
}

function handleObject(node) {
  console.log(JSON.stringify(node, null, 2))
    
  for (const [key] of Object.entries(node)) {
    inspectRecursively(node[key]);
  }
}

function handleString(node) {
  console.log(node)
}

function handleNumber(node) {
  console.log(node.toString().padStart("0", 3))
}

inspectRecursively(obj)

Say that I think that the file has grown too large. I split it up into modules

main.js

import { importRecursively } from "./importRecursively"

const obj = {
  prop1: {
    prop2: 1,
    prop3: {
      prop4: 2
    }
  },
  prop5: "1"
}
 inspectRecursively(obj)

importRecursively.js

import { handleObject } from "./handleObject.js"
import { handleString} from "./handleString.js"
import { handleNumber} from "./handleNumber.js"

function inspectRecursively(node) {
  switch (typeof node) {
    case "object":
      handleObject(node);
      break;
    case "string":
      handleString(node);
      break;
    case "number":
      handleNumber(node);
      break;
    default:
      break;
  }
}

handleObject.js

import { importRecursively } from "./importRecursively"

function handleObject(node) {
  console.log(JSON.stringify(node, null, 2))
    
  for (const [key] of Object.entries(node)) {
    inspectRecursively(node[key]);
  }
}

handleString.js

function handleString(node) {
  console.log(node)
}

handleNumber.js

function handleNumber(node) {
  console.log(node.toString().padStart("0", 3))
}

Now I end up with a circular dependency.

main -> inspectRecursively -> handleObject -> importRecursively

I think this is bad, but am not sure about it?

What do I do in this situation? Do I change something to avoid the circular dependency?

like image 917
user1283776 Avatar asked Sep 20 '25 22:09

user1283776


1 Answers

I think this is bad, but am not sure about it?

No, it's not bad. ES6 modules do handle circular dependencies like this totally fine. Just make sure that all of these modules are pure and only contain function declarations, not top-level code that constructs values that depend on imported values: declarations are "hoisted" across modules.

Do I change something to avoid the circular dependency?

You could, by using explicit dependency injection:

// inspectRecursively.js
import { makeHandleObject } from "./handleObject.js"
import { handleString} from "./handleString.js"
import { handleNumber} from "./handleNumber.js"

const handleObject = makeHandleObject(inspectRecursively);

function inspectRecursively(node) {
  …
}
// handleObject.js

export function makeHandleObject(inspectRecursively) {
  return function handleObject(node) {
    …
  };
}
like image 132
Bergi Avatar answered Sep 22 '25 13:09

Bergi