我有一个大型应用程序需要确保在调用依赖于所述已加载项目的其他例程之前(在不同时间,而不仅仅是在启动时)加载各种项目。我发现问题的是我的架构最终是如何支持这个:它要么充满了回调(和嵌套的回调!),要么预先填充了几十个整洁的小
private function SaveUser_complete(params:ReturnType):void
{
continueOnWithTheRoutineIWasIn();
}
等等。现在代码库可能只有2500行,但它可能会增长到大约10k。我只是看不到任何其他方式,但它似乎是错误的(和辛苦)。此外,我已经研究过pureMVC,Cairngorm,这些方法看起来同样乏味,除了另一层抽象。有什么建议吗?
答案 0 :(得分:1)
异步操作总是会对代码库产生影响,遗憾的是,你可以做的事情并不多。如果您的加载操作形成某种“服务”,那么最好创建一个IService接口,以及相应的MVC Style架构并使用数据令牌。简言之:
//In your command or whatever
var service:IService = model.getService();
var asyncToken:Token = service.someAsyncOperation(commandParams);
//some messaging is used here, 'sendMessage' would be 'sendNotification' in PureMVC
var autoCallBack:Function = function(event:TokenEvent):void
{
sendMessage(workOutMessageNameHere(commandParams), event.token.getResult());
//tidy up listeners and dispose token here
}
asyncToken.addEventListener(TokenEvent.RESULT, autoCallBack, false, 0, true);
我写了'workOutMessageNameHere()'这个词,我假设你想要自动化的部分,你可以有一些巨大的开关,或者一个commandParams(urls或者其他)的地图来消息名称,无论哪种方式最好从模型中获取此信息(在同一命令中):
private function workOutMessageNameHere(commandParams):String
{
var model:CallbackModel = frameworkMethodOfRetrivingModels();
return model.getMessageNameForAsyncCommand(commandParams);
}
这应该只是让你调用命令'callService'或者你正在触发它,你可以在代码中或者可能通过解析的XML配置callbackMap / switch。 希望这能让你开始,正如我刚才意识到的那样,是否相关?
编辑: 嗨,刚刚再次阅读了您要解决的问题,我认为您正在描述一系列有限状态,即状态机。 似乎大致你的序列是FunctionState - > LoadingState - > ResultState。这可能是管理小异步“链”负载的更好的通用方法。
答案 1 :(得分:0)
同意enzuguri。无论如何,你都需要大量的回调,但是如果你可以为它们定义一个单独的接口并将代码推送到控制器类或服务管理器中并将它们集中在一个地方,它就不会变得无法应对。
答案 2 :(得分:0)
我知道你在经历什么。不幸的是,我从未见过很好的解决方案。基本上异步代码只是以这种方式结束。
一种解决方案算法:
static var resourcesNeededAreLoaded:Boolean = false;
static var shouldDoItOnLoad:Boolean = false;
function doSomething()
{
if(resourcesNeededAreLoaded)
{
actuallyDoIt();
}
else
{
shouldDoItOnLoad = true;
loadNeededResource();
}
}
function loadNeededResource()
{
startLoadOfResource(callBackWhenResourceLoaded);
}
function callBackWhenResourceLoaded()
{
resourcesNeededAreLoaded = true;
if(shouldDoItOnLoad)
{
doSomething();
}
}
这种模式允许您进行延迟加载,但您也可以在必要时强制加载。这种一般模式可以被抽象出来,并且它可以正常工作。注意:一个重要的部分是从加载回调调用doSomething()
而不是actuallyDoIt()
,原因如果您不希望代码不同步,这将是显而易见的。
如何抽象上述模式取决于您的具体用例。您可以拥有一个管理所有资源加载和获取的类,并使用映射来管理加载的内容和不加载的内容,并允许调用者在资源不可用时设置回调。 e.g。
public class ResourceManager
{
private var isResourceLoaded:Object = {};
private var callbackOnLoad:Object = {};
private var resources:Object = {};
public function getResource(resourceId:String, callBack:Function):void
{
if(isResourceLoaded[resourceId])
{
callback(resources[resourceId]);
}
else
{
callbackOnLoad[resourceId] = callBack;
loadResource(resourceId);
}
}
// ... snip the rest since you can work it out ...
}
我可能会使用事件而不是回调,但这取决于你。有时,管理所有资源的中心类是不可能的,在这种情况下,您可能希望将加载代理传递给能够管理算法的对象。
public class NeedsToLoad
{
public var asyncLoader:AsyncLoaderClass;
public function doSomething():void
{
asyncLoader.execute(resourceId, actuallyDoIt);
}
public function actuallyDoIt ():void { }
}
public class AsyncLoaderClass
{
/* vars like original algorithm */
public function execute(resourceId:String, callback:Function):void
{
if(isResourceLoaded)
{
callback();
}
else
{
loadResource(resourceId);
}
}
/* implements the rest of the original algorithm */
}
同样,将上述内容从使用回调更改为事件并不难(我更倾向于实践,但为此编写简短的示例代码更难)。
重要的是要看到上述两种抽象方法仅仅封装了原始算法。这样您就可以根据自己的需要定制一种方法。
最终抽象的主要决定因素取决于:
答案 3 :(得分:0)
在我的一个项目中,我构建了基本上是包装类的自定义加载器。我发送它的元素数组加载并等待完整或失败的事件(进一步我修改它并添加优先级)。所以我没有为所有资源添加这么多处理程序。
您只需要监控已下载的所有资源以及所有资源何时完成,调度自定义event-resourceDownloaded或其他resourcesFailed。
你也可以在每个资源上加上一个标志,表明它是必要的或强制的,如果不是强制性的,不要在失败的情况下抛出失败的事件并继续监控其他资源!
现在优先考虑,你可以拥有一堆你想先显示的文件,显示并继续在后台加载其他资源。
你可以这样做,相信我你会喜欢使用它!
答案 4 :(得分:0)
您可以查看Masapi框架,看它是否满足您的需求。
您还可以调查源代码,了解他们如何解决问题。
http://code.google.com/p/masapi/
编写和维护得很好。我在使用Air开发的桌面RSS客户端中成功使用它。
假设您在并行加载太多资源的同时注意开销,它的效果非常好。