2010-09-13 3 views
2

Je suis sur le point de passer beaucoup de temps à résoudre un problème, et je me demande s'il existe une recette pour cela. C'est une application basée sur un navigateur (JavaScript et Dojo Toolkit) qui est un client d'un service Web RESTful.Recherche de recette: Plusieurs requêtes Ajax asynchrones en JavaScript

Il utilise Comet pour mettre à jour automatiquement l'affichage. Il existe une fonction de rappel qui traite tous les messages reçus. [Détails de Boring de Comet: comme une sorte d'opération d'arrière-plan, une requête HTTP est faite au serveur. Cette requête bloque sur le serveur jusqu'à ce qu'il ait un message pour nous. Lorsque le client reçoit finalement la réponse, il appelle la fonction de rappel, puis la requête HTTP suivante. HTTP autorise jusqu'à deux requêtes simultanées, cette requête "background" ne bloque donc pas les requêtes "de premier plan" qui se produisent lorsque l'utilisateur fait des choses.]

Il existe une couche d'adaptateur en dessous de la couche d'interface utilisateur. La couche d'interface utilisateur pense que les messages lui sont envoyés. La couche d'adaptateur effectue les requêtes Comet et transforme la réponse de ce que le serveur envoie à ce que la couche d'interface utilisateur attend.

var ourEventFilter = dojo.hitch(this, function(evt) { 
    if (evt["obj"]) { 
     evt.obj = this.transform(evt.obj); 
    } 
    callUIEventHandler(evt); 
} 

[dojo.hitch() est le sucre syntaxique pour faire une fermeture, la liaison this de fonction.]

Le obj peut ressembler à ceci:

{ 
    "resources": [ 
     {"name":"Me", "type":"vm", "link":"http://server/item/ABC"}, 
     {"name":"You", "type":"real", "link":"http://server/item/123"}], 
    "subObjs": [ 
     "resources":[{"name":"Him", "type":"vm", "link":"http://server/item/DEF"} 
    ] 
} 

La transformation des tours de fonction dans ce:

{ 
    "resources": [ 
     {"name":"You", "type":"real", "link":"http://server/item/123"}, 
    ], 
    "vms": [ 
     {"name":"Me", "type":"vm", "link":"http://server/item/ABC"}], 
    "subObjs:" [ 
     "resources":[], 
     "vms": [{"name":"Him","type":"vm", "link":"http://server/item/DEF"}] 
    ] 
} 

Nous trouvons les "ressources" qui sont de type "vm" et les déplacent dans un tableau séparé. Jusqu'ici tout va bien. La fonction de transformation est assez simple. C'est récursif parce que subOjbs peut contenir des subObjs.

Mais maintenant nous avons besoin de plus d'informations sur le vms. Nous devons faire des appels Ajax au serveur pour obtenir ces informations:

{ 
    "resources": [ 
     {"name":"You", "type":"real", "link":"http://server/item/123"}], 
    "vms": [ 
     {"name":"Me", "type":"vm", "link":"http://server/item/ABC", "moreInfo":"X"}], 
    "subObjs:" [ 
     "resources":[], 
     "vms": [{"name":"Him","type":"vm", "link":"http://server/item/DEF", 
       "moreInfo":"Y"}] 
    ] 
} 

La fonction de transformation ressemble à quelque chose comme ceci:

transform: function(obj) { 
     var vms=[]; 
     var newResources = []; 
     // Recurse on subObjs 
     if (obj.subObjs) { 
     for (var kx = 0; kx < obj.subObjs.length; kx++) { 
      ojb.subObjs[kx] = this.transform(obj.subObjs[kx]); 
     } 
     // Move vms out of resources into vms 
     if (obj.resources) { 
     for (var jx = 0; jx < obj.resources.length; jx++) { 
      if (obj.resources[jx].type == "vm") { 
       var thisVM = obj.resources[jx]; 
       // Note: more info needed here. 
       //thisVM = this.getMoreInfo(thisVM); 
       vms.push(thisVM); 
      } else { 
       newResources.push(obj.resources[jx]; 
      } 
     } 
     obj.vms = vms; 
     obj.resources = newResources; 
     } 
     return obj; 
} 

Et maintenant, nous avons un problème. Comment est-ce que j'ai écrit getMoreInfo()?

je pouvais faire des appels synchrones à ce point:

getMoreInfo: function(vm) { 
    vmObj = callServerSynchronouslyToGET(vm.link); 
    vm.moreInfo = vmObj ? vmObj.moreInfo : null; 
} 

Mais les appels synchrones ne sont jamais une bonne idée à Ajax (il serait Sjax).

Je ne pense pas qu'il soit possible d'écrire getMoreInfo() en tant que tel pour faire des appels asynchrones. Je dois remonter à travers plusieurs couches de l'oignon et réécrire tout d'un certain point vers le bas, j'espère sans rien réécrire au-dessus du calque Comet-callback.

Je connais une recette qui transforme une fonction récursive en une fonction non récursive. Existe-t-il une recette qui transforme un oignon avec une boucle avec GET synchrone au centre en une chaîne de GET asynchrones?

+0

"HTTP autorise jusqu'à deux requêtes simultanées": vrai, mais en réalité, la plupart des navigateurs modernes permettent plus de deux requêtes simultanées: http://www.spasche.net/files/parallel_connections/ "ça serait Sjax": en fait , ce serait "Sjaj", car il n'y a pas de XML impliqué ici, mais JSON. ;) BTW, dans vos exemples, la condition 'evt.obj [" type "]' (notez également la parenthèse manquante), qui est identique à 'evt.obj.type', retournera toujours' false', comme 'type 'n'est pas un descendant direct de' evt.obj'. –

+0

Notez également que lorsque 'evt.obj.type' contient une valeur falsifiée, y compris une chaîne vide, le bloc d'instructions de votre condition sera probablement ignoré. Et notez que l'objet 'item' solitaire que vous avez utilisé devrait probablement être' obj'. –

+0

Le code a été simplifié à partir du code réel. Certaines erreurs ont été commises dans la transcription. (J'ai changé de nom mais je ne les ai pas tous saisis.) –

Répondre

1

Il existe une extension inter-navigateur pour le langage JavaScript StratifiedJS.

Il est conçu pour résoudre précisément le problème que vous mentionnez: Il vous permet de programmer de manière synchrone tout ce qui est exécuté de manière asynchrone sous le capot.

La bibliothèque JS qui active StratifiedJS sur le navigateur s'appelle "Oni Apollo". Voir http://onilabs.com/apollo pour plus de détails.

Dans votre cas, vous pouvez convertir l'ensemble de votre oignon asynchrone en code synchrone en utilisant StratifiedJS ou vous pouvez conserver votre logique existante en collant simplement dans un élément de script « text/SJS »:

<script src="http://code.onilabs.com/latest/oni-apollo.js"></script> 

<script type="text/sjs"> 

    // your existing code here 

    getMoreInfo: function(vm) { 
    var vmObj = require('http').get(vm.link); 
    vm.moreInfo = vmObj ? vmObj.moreInfo : null; 
    } 
</script> 

ici , require ('http'). Get() effectue un XHR asynchrone sous le capot (pour plus de détails, voir les documents apollo api au lien ci-dessus).

Questions connexes