How to solve this basic ES6-module circular dependency problem?
/#!/JoePea
joe at trusktr.io
Wed Aug 10 06:46:19 UTC 2016
When I try this same code with Webpack, I get the *exact same results*: the
`console.log` statements in the exact same order, where the last output
shows that `A` in the entry point is `undefined`).
Am I misunderstanding something about live bindings? Is there some
guaranteed order in which these modules should be evaluated?
The reason why I'm after a solution for the circular dependency is because
in my real-world case I need to use `instanceof A` and `intanceof B` within
the `C` superclass defined in module C. This is a case of the Fragile Base
Class Problem where a class should usually *not* have knowledge of it's
subclasses, but the base class in my case is intended to be internal only,
not a part of the public API that end users will extend from.
*/#!/*JoePea
On Tue, Aug 9, 2016 at 11:12 PM, /#!/JoePea <joe at trusktr.io> wrote:
> I can get the whole thing to work if I pass the C dependency into the
> `setUpA` and `setUpB` functions as follows, but oddly `A` is `undefined` in
> the Entrypoint module at the `console.log` statement, which makes it seem
> to me like live bindings aren't working the I was expecting.
>
> ```js
> // --- Entrypoint
>
> import A from './app/A'
> console.log('Entrypoint', A) // HERE, output: "Entrypoint undefined"
> ```
>
> ```js
> // --- Module A
>
> import C from './C'
>
> let A
>
> export
> function setUpA(C) {
>
> console.log('setUpA')
> console.log(C)
>
> A = class A extends C {
> // ...
> }
>
> }
>
> console.log('Module A', C, setUpA)
>
> export {A as default}
> ```
>
> ```js
> // --- Module B
>
> import C from './C'
>
> let B
>
> export
> function setUpB(C) {
>
> console.log('setUpB', C)
>
> B = class B extends C {
> // ...
> }
>
> }
>
> console.log('Module B', C, setUpB)
>
> export {B as default}
> ```
>
> ```js
> // --- Module C
>
> import A, {setUpA} from './A'
> import B, {setUpB} from './B'
>
> let C = class C {
> constructor() {
> // this may run later, after all three modules are evaluated, or
> // possibly never.
> console.log(A)
> console.log(B)
> }
> }
>
> setUpA(C)
> console.log('Module C', A)
>
> setUpB(C)
> console.log('Module C', B)
>
> export {C as default}
> ```
>
> */#!/*JoePea
>
> On Tue, Aug 9, 2016 at 9:59 PM, /#!/JoePea <joe at trusktr.io> wrote:
>
>> It seems that the environment I'm in (Meteor uses [reify](
>> https://github.com/benjamn/reify)) tries to evaluate A and B first, so I
>> thought I could take advantage of "live bindings" by changing my modules to
>> the following:
>>
>> ```js
>> // --- Entrypoint
>>
>> import A from './app/A'
>> console.log('Entrypoint', A)
>> ```
>>
>> ```js
>> // --- Module A
>>
>> import C from './C'
>>
>> let A
>>
>> export
>> function setUpA() {
>>
>> console.log('setUpA')
>> console.log(C)
>>
>> A = class A extends C {
>> // ...
>> }
>>
>> }
>>
>> console.log('Module A', C, setUpA)
>>
>> export {A as default}
>> ```
>>
>> ```js
>> // --- Module B
>>
>> import C from './C'
>>
>> let B
>>
>> export
>> function setUpB() {
>>
>> console.log('setUpB', C)
>>
>> B = class B extends C {
>> // ...
>> }
>>
>> }
>>
>> console.log('Module B', C, setUpB)
>>
>> export {B as default}
>> ```
>>
>> ```js
>> // --- Module C
>>
>> import A, {setUpA} from './A'
>> import B, {setUpB} from './B'
>>
>> let C = class C {
>> constructor() {
>> // this may run later, after all three modules are evaluated, or
>> // possibly never.
>> console.log(A)
>> console.log(B)
>> }
>> }
>>
>> setUpA()
>> console.log('Module C', A)
>>
>> setUpB()
>> console.log('Module C', B)
>>
>> export {C as default}
>> ```
>>
>> As you can see, modules A and B simply export the code that should be
>> evaluated (note the live bindings). Then finally, the C module is evaluated
>> last. At the end of the C module, you see that it calls `setUpA` and
>> `setUpB`. When it fires `setUpA`, an error is thrown on the second
>> `console.log` that `C` is undefined (or, specifically, `C.default` is
>> `undefined` because the ES6 modules are compiled into CommonJS form).
>>
>> I thought that if `C` was a live binding, then it should be ready by the
>> time the `setUpA` function is called. Should this in fact be the case?
>>
>> */#!/*JoePea
>>
>> On Tue, Aug 9, 2016 at 5:36 PM, John Lenz <concavelenz at gmail.com> wrote:
>>
>>> Without a way to load "later" (aka "soft") dependencies, ES6 module will
>>> continue to be more or less broken for circular dependencies.
>>>
>>> On Tue, Aug 9, 2016 at 4:11 PM, Tab Atkins Jr. <jackalmage at gmail.com>
>>> wrote:
>>>
>>>> On Tue, Aug 9, 2016 at 4:00 PM, /#!/JoePea <joe at trusktr.io> wrote:
>>>> > True, and so that's why I'm wondering if the module system can see
>>>> that it
>>>> > can satisfy all module requirements if it simply evaluates module C
>>>> first,
>>>> > followed by A or B in any order. It is easy for us humans to see
>>>> that. It
>>>> > would be nice for the module system to see that as well (I'm not sure
>>>> if
>>>> > that is spec'd or not).
>>>>
>>>> That knowledge requires, at minimum, evaluating the rest of each
>>>> module, beyond what is expressed in the `import` statements. That's
>>>> assuming there's no dynamic trickery going on that would invalidate
>>>> whatever assumptions it can draw from surface-level analysis.
>>>>
>>>> Because of this, only the `import` statements are declaratively
>>>> available to the module system to work with. Based on that, it
>>>> definitely can't make any ordering assumptions; all it knows is that A
>>>> imports C, B imports C, and C imports both A and B, making a circular
>>>> import.
>>>>
>>>> ~TJ
>>>> _______________________________________________
>>>> es-discuss mailing list
>>>> es-discuss at mozilla.org
>>>> https://mail.mozilla.org/listinfo/es-discuss
>>>>
>>>
>>>
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20160809/6999f0e8/attachment-0001.html>
More information about the es-discuss
mailing list