Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

循环依赖的问题再仔细考虑下 #893

Closed
lifesinger opened this Issue · 36 comments
@lifesinger
Owner

感觉运行时的循环依赖,依旧可以像 Node.js 一样提供支持。

下载时的循环依赖,则适时打断下载链就好。

@afc163
Owner

下载时循环依赖的处理,建议采用先入为主的方式,即 a 依赖 b,b 依赖 c ,下载 c 后如果发现依赖 b 和 a,则无视之。

@kmvan

依赖循环这个逻辑关系本身就是不是有矛盾?

@zero7u

坐等事态发展,支持循环依赖万岁!虽然帮不上啥忙,哎,真心难过。

@zero7u

@afc163 术业有专攻,你们研究的太深入,接近真理了,我从山脚下只能仰望。
有循环依赖的文件,如何加载,加载了是不是马上执行,会不会出错,和requirejs的区别,一切都是黑匣子。
运行时有循环依赖,也是两眼一抺黑。
所以,只能无耻的坐享其成了!倒很希望能帮点忙,有一点点经验。

@afc163
Owner

也没啥好难过的,没有你的抗争,都不会有这个 issue 。

@zero7u

@afc163 嘿嘿,其实我怕这个问题你们有举棋不定的心情,循环依赖的不支持,可能会阻碍了SeaJs的发展呢也说不定,毕竟前端越来越复杂嘛。
当然,如果这真不是个问题,也不会有这个issue :smile:

@afc163
Owner

我个人是不太支持循环依赖的写法的,感觉很不舒服。

去掉循环依赖后,除了 @zero7u , 貌似也没多少人有异议。可以再看看。

@zero7u

@afc163 我会找到我的小伙伴们的,来支援我。
当初我们设计html5的2d贴图游戏引擎底层的过程中,也都避免了循环依赖。可是后来,代码越来越多,功能越来越多,到了几w行,功能就有了交叉,特别是一些原来做过Java的同学,就很不理解为啥不能支持循环依赖?
我承认是代码结构不太理想,可以重构,可对于多数人来说,这是很痛苦的过程。不能要求使用者的水平都很高,工具应该尽可能的灵活,像Java一样,是个人就会写,降低了门槛,才能更流行。
SeaJs是为大家服务的,上面是只一家之言,但相信我也有私心的为了SeaJs好!
如果支持循环依赖,也可以从规范上不推荐?或者引导我们这些低水平的使用者,用一种好的设计模式,取代可能出现循环依赖的地方。

@zero7u

@afc163 对于HTML5游戏来说,我也是有点心得,在群里也小有威望,我想说的是,我代表一部分人,随着H5的发展,这些人会越来越多。
而针对H5游戏来讲,好的思路一般来自其他语言,即要做好H5游戏,需要从flash、Java、C++等语言吸取经验,甚至将一些好的实现直接翻译过来,而他们是没有循环依赖问题的,这可能就阻止了一部分人选择SeaJs。

@edokeh

@zero7u
我们实际上不是一个圈子的,场景完全不同,有些事情其实很难说服啊

@lifesinger 是不是多考虑考虑游戏圈的同志们

@zero7u

@edokeh 公道话,呵呵,谢谢你啊!
提到很多Java,因为我之前做Java,C++么的都不会。现在就是想把js向Java方向靠,最重要就看中他两点:面向对象和包管理。分别对应aralejs/classSeaJs(个人的匹配)。
当然我认为Java还是很有代表性的,但Java中高手,可能某些架构师写的代码,也没有考虑循环依赖的问题,因为就没这问题嘛,可是这可能就让我们一部分做游戏的,需要从他们汲取营养的,多了一层障碍。
希望 @lifesinger 做调研的时候,多考虑一下这类人哈。只是个人见解,事实为主!

@gyf19

呵呵 @lifesinger 对于一个优秀的模块化加载器来说,循环引用还是需要的。 SeaJs就是最优秀的模块化加载器,循环引用功能可以有的。 哈哈

@robinma

刚用模块化写时,会遇到循依赖的问题,现在几乎遇不到,我觉得主要来是模块设计的问题。

@suncn-demo

循环依赖看似不可理解,但确实是一个客观存在。

@zero7u

@robinma 不想说太多,以一个前端经验4年的勤奋小白来说,我拿几个游戏作品来举例子吧:
坦克大战打飞机极限空战。坦克大战、打飞机这种难度的设计,完成可以不用模块加载器,更没有依赖的问题; 极限空战这个难度,却让我绞尽脑汁也没有完全解决循环依赖的问题(2w行代码)。你可以说是模块设计的问题,事实上也有问题,可是对于绝大多数人来说,根本避免不了,就算刻意的回避了,结构也不一定最优。这游戏原来是安卓端游,一个主程写的,可能水平也不怎么样吧。你说呢?

@semious

个人觉得,循环引用的危害在于 模块之间的方法调用形成闭环,造成程序无限循环。就模块这个层次来说,即使看上去是模块循环依赖,但实质可能也没有任何问题,如果在runtime期间能发现模块方法调用形成的闭环,能有效解决 循环引用问题。 其实 模块之间的关系 基本可以分为两个关系,平级关系和父子关系,父子关系一般情况下,很少会出现 循环引用 ,当然可能出现循环树,不过这种情况比较罕见。而平级关系的模块就很有可能出现 循环引用。如果为了避免 循环引用 而刻意将 平级关系的模块设计成 父子关系,或者再加一个模块 和他们形成父子关系,都会增加逻辑的复杂度和 结构的复杂,为今后排除问题、需求更改 ,造成一定隐患。

@lifesinger
Owner

感谢大家关注,近期我先搞一个初步版本出来,到时再请大家 review 下是否合适。

暂定 2.2 版本加入。

@lianqin7
Collaborator

@lifesinger

这个对性能会有多大影响?我比较关心这个。。

@lifesinger
Owner

@lianqin7 前提条件是不能影响性能,不会增加对循环依赖的检查(这个依旧交给插件去做),只是保留运行时的及时返回机制(拿到什么就返回什么,现在是运行时停下来了)

大概想清楚了怎么实现,不用怎么动代码。

@zero7u

加载时的循环依赖不好解决,加上插件检查,能正常运行吗?使用Grunt构建之后,是不是就没有加载问题了呢?

@gyf19

@lifesinger 循环依赖的问题可以在 2.2 版本加入吗?

@army8735
Owner

放入3.0

@army8735 army8735 closed this
@lifesinger
Owner
@army8735 army8735 modified the milestone: 3.0, 2.2.0
@army8735 army8735 reopened this
@army8735
Owner

在pass-entry分支中尝试了新的传递引用算法,相比过去的递归依赖回溯算法,原生支持了和NodeJS一样的循环引用,性能也相差无几。甚至在1000个模块依赖测试中性能更好了——因为减少了回溯过程,模块只正向传递入口引用,叶子节点会直接通知入口

@army8735 army8735 removed this from the 2.3.0 milestone
@iamweilee

请问seajs如何处理循环依赖的?
比如如下代码:
spinning.js:
define(function(require, exports, module) {

var $ = require('jquery');
var aaa = require('./test1');//这里是为了测试循环依赖而加入的代码

function Spinning(container) {
this.container = $(container);
this.icons = this.container.children();
this.spinnings = [];
}

module.exports = Spinning;
……
});

test1.js:
define(function(require, exports, module) {

var aaa = require('./spinning');//测试循环依赖

function test() {
return aaa;
}

module.exports = test;

});

这是官方的Hello Seajs的示例。我稍微做了修改。然后就有问题了。调试的时候发现main、spinning和test1的_remain=1、status = 3。所以不知道seajs是如何处理这种问题的?

@army8735
Owner

2.3通过一个插件。
但master上最新源代码已经变了,你可以去看看。

@iamweilee

谢谢!
循环依赖脚本的加载是解决了。但源码:
// Execute a module
Module.prototype.exec = function () {
var mod = this

// When module is executed, DO NOT execute it again. When module
// is being executed, just return module.exports too, for avoiding
// circularly calling
if (mod.status >= STATUS.EXECUTING) {
return mod.exports
}
……

这里处理循环调用的时候是直接返回mod.exports,一个空对象。
那么在require该模块后若紧跟着接调用获取到的模块的方法或变量就会抛异常。
比如:
spinning.js:
define(function(require, exports, module) {

var $ = require('jquery');
var aaa = require('./test1');//这里是为了测试循环依赖而加入的代码

function Spinning(container) {
this.container = $(container);
this.icons = this.container.children();
this.spinnings = [];
}

module.exports = Spinning;
……
});

test1.js:
define(function(require, exports, module) {

var Spinning= require('./spinning');//测试循环依赖
var s1 = new Spinning('#container');//这里会抛出异常:Uncaught TypeError: object is not a function
s1.render();

function test() {
return aaa;
}

module.exports = test;

});
这个问题有没有好的解决方法。如果解决不了这个问题,那么循环依赖存在的意义是什么。或者说如何达到开发者在使用循环依赖时希望得到的结果。

@army8735
Owner

这和node.js目前一样,只是实现了简单循环依赖。一般你这种直接初始化时循环使用引用的场景不会太多,都是在某种条件下触发(如onclick),如此便没有问题。如果要解决初始化时的循环引用,便会难很多,毕竟node.js都没有实现,至少为了保持一致性,暂不会考虑它。

@iamweilee

可是即使是某种条件下出发(如onclick) 那么在onclick的事件处理函数里拿到的模块也是mod.exports直接返回的空对象。seajs似乎并没有在直接返回mod.exports并且循环依赖的模块都执行完成后去修改它成为真正的模块引用。

@army8735
Owner

哦对,只有在执行一次后才能初始化正确的mod.exports。复杂循环引用实现太难,没啥好的算法,如果有就谢天谢地了,甚至可以pull回给node.js。

@iamweilee

啊呃,若遇此问题只能暂且在模块中暴露一个方法用来修改真实的模块引用了。

@kmvan
@army8735
Owner

引用形成一个闭环,目前我还没写过这样的,但是在游戏开发中呼声非常高。

@xufei

总觉得循环引用是个很不合理的需求,产生这种需求的根本原因是代码结构不好。Java开发人员应该更写不出循环引用的代码才对,因为解耦的方法很多,比如依赖注入。

@army8735 army8735 closed this
@tcdona

同问,

能否举个例子描述一下循环依赖的应用场景?

我的理解是代码规模复杂到一定程度经常会无法避免循环依赖的情况。
请问现在2.3版本中的那个算法,具体是做了哪些事情
或者说“简单循环依赖”指的是怎样一种情况,适用和不适用的场景分别是啥呢?

还有1点提一下,觉得2.3比之前的版本有本质的改进了,很想马上用起
我认为改动挺大的,但没有找到好的quick start姿势,
是不是重心放在3.0上啦,我们是不是继续等3.0呢。

@army8735
Owner

应用场景我无法提供,呼声高的是那些游戏开发人员。

3.0早呢。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.