How to handle circular dependencies? [duplicate]
Asked Answered
S

1

1

Given:

  • StringPreconditions extends ObjectPreconditions
  • ObjectPreconditions depends on StringPreconditions (one of its methods returns the subclass)
  • Preconditions is a gatekeeper to ObjectPreconditions and StringPreconditions (making sure they both load before returning an instance)
  • User depends on Preconditions

I have this code:

define(["ObjectPreconditions"], function(ObjectPreconditions)
{
  console.log("Inside StringPreconditions");

  function StringPreconditions() {}
  StringPreconditions.prototype = Object.create(ObjectPreconditions.prototype);
  StringPreconditions.prototype.constructor = ObjectPreconditions;
  return StringPreconditions;
});

define(["require"], function(require)
{
  console.log("Inside ObjectPreconditions");

  // Resolve circular dependencies
  var StringPreconditions;
  require(["StringPreconditions"], function(theStringPreconditions)
  {
    StringPreconditions = theStringPreconditions;
    console.log("ObjectPreconditions finished loading StringPreconditions");
  });

  function ObjectPreconditions() {}
  ObjectPreconditions.prototype.isInstanceOf(type)
  {
    console.log("ObjectPreconditions.isInstanceOf() invoked");
    if (type === String)
      return new StringPreconditions();
  }
});

define(["ObjectPreconditions", "StringPreconditions"], function(ObjectPreconditions, StringPreconditions)
{
  console.log("Inside Preconditions");
  var Preconditions = {};

  Preconditions.requireThat(parameter) = function()
  {
    return new ObjectPreconditions(parameter);
  };
  return Preconditions;
});

define(["Preconditions"], function(Preconditions)
{
  console.log("Inside User");
  function User() {}
  User.prototype.doSomething = function()
  {
    var StringPrecondition = Preconditions.requireThat("test").isInstanceOf(String);
  }
});

The problem is that 10% of the time I get this load order:

  • Inside User
  • Inside Preconditions
  • Inside ObjectPreconditions
  • Inside StringPreconditions
  • ObjectPreconditions.isInstanceOf() (CRASH because StringPreconditions is undefined)
  • ObjectPreconditions finished loading StringPreconditions

I've already read http://requirejs.org/docs/api.html#circular but I believe they are doing the same thing I am.

Any ideas?

Stansberry answered 7/11, 2014 at 19:49 Comment(0)
S
1

UPDATE: https://mcmap.net/q/261397/-how-to-fix-this-es6-module-circular-dependency contains an updated answer for ES6 modules.


I figured it out: We need to create a "gatekeeper" file that will define functions that depend on the circular dependencies.

  1. Rename ObjectPreconditions.js to AbstractObjectPreconditions.js.
  2. Create a new ObjectPreconditions.js file (our new gatekeeper).
  3. Move any circular dependencies out of AbstractObjectPreconditions.js into ObjectPreconditions.js
  4. User code should require(ObjectPreconditions). Code involved in the circular dependency (e.g. subclasses) should require(AbstractObjectPreconditions).

Here is what the resulting code looks like:

define(["AbstractObjectPreconditions"], function(ObjectPreconditions)
{
  console.log("Inside StringPreconditions");

  function StringPreconditions() {}
  StringPreconditions.prototype = Object.create(ObjectPreconditions.prototype);
  StringPreconditions.prototype.constructor = ObjectPreconditions;
  return StringPreconditions;
});

define(["require"], function(require)
{
  console.log("Inside AbstractObjectPreconditions");

  function ObjectPreconditions() {}
  return ObjectPreconditions;
});

define(["AbstractObjectPreconditions"], function(ObjectPreconditions)
{
  // Gatekeeper for circular dependencies
  ObjectPreconditions.prototype.isInstanceOf(type)
  {
    console.log("ObjectPreconditions.isInstanceOf() invoked");
    if (type === String)
      return new StringPreconditions();
  }

  return ObjectPreconditions;
});


define(["ObjectPreconditions", "StringPreconditions"], function(ObjectPreconditions, StringPreconditions)
{
  console.log("Inside Preconditions");
  var Preconditions = {};

  Preconditions.requireThat(parameter) = function()
  {
    return new ObjectPreconditions(parameter);
  };
  return Preconditions;
});

define(["Preconditions"], function(Preconditions)
{
  console.log("Inside User code");
  function User() {}
  User.prototype.doSomething = function()
  {
    var StringPrecondition = Preconditions.requireThat("test").isInstanceOf(String);
  }
});
Stansberry answered 7/11, 2014 at 20:5 Comment(1)
this may be correct, but I find very difficult to follow, can you simplify variable names etc?Undesigning

© 2022 - 2024 — McMap. All rights reserved.