2009-08-10 6 views
1

J'ai une grande application qui doit s'assurer que divers éléments sont chargés (à différents moments, pas seulement au démarrage) avant d'appeler d'autres routines qui dépendent des éléments chargés. Ce que je trouve problématique est de savoir comment mon architecture finit cherche à soutenir ceci: il est soit jonché de callbacks, ou peuplée avec des dizaines de joli petitas3 chargement architecture

private function SaveUser_complete(params:ReturnType):void 
{ 
     continueOnWithTheRoutineIWasIn(); 
} 

et ainsi de suite (et callbacks imbriqués!). À l'heure actuelle, la base de code n'est peut-être que de 2 500 lignes, mais elle va probablement atteindre environ 10 000 lignes. Je ne vois pas d'autre moyen de contourner cela, mais il me semble que c'est faux (et laborieux). Aussi, j'ai regardé dans pureMVC, Cairngorm, et ces méthodes semblent également fastidieuses, sauf avec une autre couche d'abstraction. Aucune suggestion?

Répondre

1

Les opérations asynchrones bien ont toujours cet effet sur les bases de code, malheureusement, il n'y a pas beaucoup de choses que vous pouvez faire. Si vos opérations de chargement forment une sorte de «Service», il est préférable de créer une interface IService, avec l'architecture de style MVC appropriée et d'utiliser des jetons de données. En bref:

//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); 

Là où j'ai écrit workOutMessageNameHere() les mots que je suppose est la partie que vous voulez automatiser, vous pouvez soit avoir une sorte de commutateur énorme, ou une carte de commandParams (urls ou autre) aux noms de message, de toute façon mieux obtenir cette information d'un modèle (dans la même commande):

private function workOutMessageNameHere(commandParams):String 
{ 
    var model:CallbackModel = frameworkMethodOfRetrivingModels(); 
    return model.getMessageNameForAsyncCommand(commandParams); 
} 

cela devrait, espérons simplement vous laisser appeler la commande « callService » ou comme vous déclenchez, vous pouvez configurer le callbackMap/switch dans le code ou éventuellement via XML analysé. J'espère que cela vous a aidé, et comme je viens de me rendre compte, est pertinent? Salut, juste eu une autre lecture du problème que vous essayez de résoudre, et je pense que vous décrivez une série d'états finis, c'est-à-dire une machine à états. Il semble que vos séquences soient en gros FunctionState -> LoadingState -> ResultState. Cela pourrait être une meilleure approche générale pour gérer des charges de petites «chaînes» asynchrones.

0

En accord avec enzuguri. Vous aurez besoin de beaucoup de rappels, mais si vous pouvez définir une seule interface pour chacun d'entre eux et insérer le code dans des classes de contrôleur ou un gestionnaire de services et l'avoir en un seul endroit, cela ne deviendra pas écrasant.

0

Je sais ce que vous traversez. Malheureusement, je n'ai jamais vu une bonne solution. Fondamentalement, le code asynchrone finit comme ça.

Un algorithme de solution:

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(); 
    } 
} 

Ce genre de modèle que vous permet de faire le chargement paresseux, mais vous pouvez également forcer une charge en cas de besoin. Ce schéma général peut être abstrait et il a tendance à fonctionner correctement. Remarque: une partie importante appelle le doSomething() du rappel de chargement et non le actuallyDoIt() pour des raisons qui seront évidentes si vous ne voulez pas que votre code devienne désynchronisé. La façon dont vous résumez le schéma ci-dessus dépend de votre cas d'utilisation spécifique. Vous pouvez avoir une seule classe qui gère tout le chargement et l'acquisition des ressources et utilise une carte pour gérer ce qui est chargé et ce qui ne l'est pas et permet à l'appelant de définir un rappel si la ressource n'est pas disponible. par exemple.J'utiliserais probablement des événements et non des rappels, mais c'est à vous de décider. Parfois, une classe centrale gérant toutes les ressources n'est pas possible, auquel cas vous pourriez vouloir passer un proxy de chargement à un objet capable de gérer l'algorithme.

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 */ 
} 

Encore une fois, il est difficile de changer ce qui précède de travailler avec callbacks à des événements (que je préférerais dans la pratique, mais il est plus difficile d'écrire court exemple de code pour cela).

Il est important de voir comment les deux approches abstraites ci-dessus ne font qu'encapsuler l'algorithme original. De cette façon, vous pouvez adapter une approche qui répond à vos besoins.

Les principaux déterminants de votre abstraction finale dépendra:

  • Qui connaît l'état des ressources ... le contexte d'appel ou l'abstraction de service? Avez-vous besoin d'une place centrale pour acquérir des ressources de ... et les tracas de faire de cette place centrale disponible tout au long de votre programme (singletons)
  • Comment compliqué est vraiment les nécessités de chargement de votre programme? (Par exemple, il est possible d'écrire cette abstraction de telle sorte qu'une fonction ne soit pas exécutée tant qu'une liste de ressources n'est pas disponible).
0

Dans l'un de mes projets, je construis un chargeur personnalisé qui était essentiellement une classe wrapper. Je lui envoyais un tableau d'éléments à charger et j'attendais un événement complet ou échoué (plus loin je l'ai modifié et ajouté la priorité aussi). Je n'avais donc pas besoin d'ajouter autant de gestionnaires pour toutes les ressources.

Vous avez juste besoin de surveiller toutes les ressources qui ont été téléchargées et quand toutes les ressources sont terminées, envoyez un event-resourceDownloaded personnalisé ou bien resourcesFailed.

Vous pouvez également mettre un drapeau avec chaque ressource en disant qu'il est nécessaire ou obligatoire ou non. Si ce n'est pas obligatoire, ne lancez pas l'événement échoué en cas d'échec de cette ressource et continuez à surveiller d'autres ressources!

Maintenant, avec la priorité, vous pouvez avoir un tas de fichiers que vous voulez afficher en premier, afficher et continuer à charger d'autres ressources en arrière-plan.

Vous pouvez faire la même chose et croyez-moi, vous apprécierez l'utiliser !!

0

Vous pouvez vérifier la structure Masapi pour voir si elle répond à vos besoins.

Vous pouvez également étudier le code source pour savoir comment ils ont abordé le problème.

http://code.google.com/p/masapi/

Il est bien écrit et maintenu. Je l'ai utilisé avec succès dans un client RSS de bureau que j'ai développé avec Air.

Cela a très bien fonctionné en supposant que vous faites attention à l'overhead tout en chargeant trop de ressources en parallèle.