Gestion D'une file D'attente en JavaScript via des callbacks

je travaille sur une page qui utilise JavaScript pour gérer une file d'attente. Mon défi est que mon code a imbriqué les callbacks. Les callbacks imbriqués m'embrouillent en ce qui concerne l'étendue de ma file d'attente. Actuellement, j'ai le code suivant:

function MyApp() {}
module.exports = MyApp;

MyApp.myQueue = [];
MyApp.queueIsLocked = false;

MyApp.enqueue = function(item, onSuccess, onFailure) {
  if (!MyApp.queueIsLocked) {
    MyApp.queueIsLocked = true;        
    MyApp.myQueue.push(item);
    MyApp.queueIsLocked = false;

    item.send(   
      function() {
        console.log('item: ' + item.id);

        MyApp.queueIsLocked = true;                      
        MyApp.findItemById(item.id,
          function(index) {
            if (index !== -1) {
              MyApp.myQueue.splice(index, 1);
              MyApp.queueIsLocked = false;

              if (onSuccess) {
                onSuccess(item.id);
              }
            }
          }
        );
      },
      function() {
        alert('Unable to send item to the server.');
        if (onFailure) {
          onFailure();
        }
      }
    );
  }
};

MyApp.findItemById = function(id, onComplete) {
  var index = -1;
  if (MyApp.queueIsLocked) {
    setTimeout(function() {
      // Attempt to find the index again.
    }, 100);
  } else {
    MyApp.queueIsLocked = true;
    for (var i=0; i<MyApp.myQueue.length; i++) {
      if (MyApp.myQueue[i].id === id) {
        index = i;
        break;
      }
    }
  }

  if (onComplete) {
    onComplete(index);
  }
};

send fonction se comporte différemment sur la base des détails de item. Parfois, l'article sera envoyé à un serveur. Parfois, il sera envoyé à plusieurs serveurs. De toute façon, je ne savoir quand l'article sera fait être "envoyé". Pour cette raison, j'utilise un rappel pour gérer la file d'attente. Quand l'article est fait étant "envoyé", je veux le supprimer de la file d'attente. Je dois utiliser un délai ou un intervalle pour vérifier si la file d'attente est verrouillée ou non. Si elle n'est pas verrouillée, je veux supprimer l'élément de la file d'attente. Cette vérification ajoute un autre niveau de nidification qui me trouble.

mon défi est, Je ne crois pas que la portée de l'index fonctionne comme je l'espérais. Je me sens comme je suis d'une race condition. Je me base sur le fait que J'ai écrit le test Jasmine suivant:

describe('Queue', function() {
  describe('Approach 1', function() {
    it('should do something', function() {
      MyApp.enqueue({id:'QRA', text:'Test A'});
    });
  });

  describe('Approach 2', function() {
    it('should successfully queue and dequeue items', function() {
      MyApp.enqueue({id:'WX1', text:'Test 1'});
      MyApp.enqueue({id:'QV2', text:'Test 2'});
      MyApp.enqueue({id:'ZE3', text:'Test 3'});
    });
  });
});

quand j'exécute le test, je vois ce qui suit dans la fenêtre de la console:

item: QRA index: 1
item: WX1 index: 2
item: QV2 index: 3
item: ZE3 index: 4

C'est comme si les articles n'étaient pas déqueutés comme je m'y attendais. Suis-je loin de la base dans ma façon de gérer une file d'attente? Ce que je fais mal?

merci de votre aide.

10
demandé sur user70192 2015-08-17 16:36:36

4 réponses

voici quelques questions auxquelles vous devez réfléchir et répondre par vous-même au sujet de votre intention et de votre conception:

  1. Il semble que la file d'attente représente les éléments que vous essayez d'envoyer au serveur. Vous ajoutez des éléments à la file d'attente qui doivent être envoyés, et vous les retirez de la file d'attente après qu'ils aient été envoyés avec succès.
  2. voulez-vous que votre code envoie plusieurs éléments simultanément, en parallèle? Par exemple, Un élément est ajouté à la file d'attente, puis envoyé. Avant l'envoi asynchrone pour une finitions, l'article B est ajouté à la liste. Le code devrait-il essayer d'envoyer l'élément B avant la fin de l'envoi de l'élément A? D'après votre code, on dirait que oui.

il semble que vous ne voulez pas vraiment/besoin d'une file d'attente, en soi, autant que vous voulez une liste pour suivre quels articles sont en cours d'envoi. "Queue" implique que les objets sont traités dans une sorte d'ordre FIFO.

si vous voulez juste pister des articles basés sur id, alors vous pouvez utiliser un objet à la place. Par exemple:

MyApp.items = {};
MyApp.addItem = function(item){
  MyApp.items[item.id] = item;
  item.send(
    function(){ // success
      MyApp.removeItem(item.id)
    }
  );
}
MyApp.removeItem = function(id){
  delete MyApp.items[id];
  onSuccess(id);
}

En outre, Je ne pense pas que vous avez besoin d'un verrouillage sur la file d'attente. Javascript est simple-threadé, donc vous n'aurez jamais un cas où deux parties de votre code essayent d'opérer sur la file d'attente en même temps. Quand un appel ajax se termine de manière asynchrone, votre code de rappel ne sera pas exécuté tant que n'importe quel autre code en cours d'exécution ne sera pas terminé.

10
répondu GreenGiant 2015-08-19 14:58:01

Le gros défaut que je vois est que vous appelez MyApp.queueIsLocked = true juste avant MyApp.findItemById. Parce qu'elle est verrouillée, la fonction met en place un timeout (qui ne fait rien), et procède à l'appel onComplete(-1). -1 est alors explicitement ignoré par onComplete, ne parvenant pas à faire la queue, et verrouillant votre file d'attente.

Vous avez probablement censé pour réessayer de le trouver, comme ceci:

setTimeout(function() {
  // Attempt to find the index again.
  MyApp.findItemById(id, onComplete);
}, 100);

Je n'en suis pas sûr, mais je pense que Jasmine a besoin d'instructions explicites pour activer les fonctions de temporisation, en utilisant jasmine.clock().tick


cela dit, je suggère de supprimer toutes les références à queueIsLocked, y compris le code de temporisation ci-dessus. Aussi, si item.id est toujours une chaîne unique, vous pouvez utiliser un objet au lieu d'un tableau pour stocker vos valeurs.

Voici une itération suggérée, restant aussi fidèle que possible à L'API originale:

function MyApp() {}
module.exports = MyApp;

MyApp.myQueue = {};

//Sends the item, calling onSuccess or onFailure when finished
//  item will appear in MyApp.myQueue while waiting for a response from send
MyApp.enqueue = function(item, onSuccess, onFailure) {
  MyApp.myQueue[item.id] = item;

  item.send(function() {
    console.log('item: ' + item.id);
    delete MyApp.myQueue[item.id];
    if (onSuccess) {
      onSuccess(item.id);
    }
  }, function() {
    alert('Unable to send item to the server.');
    if (onFailure) {
      onFailure();
    }
  });
};

//Returns the Item in the queue, or undefined if not found
MyApp.findItemById = function(id, onComplete) {
  if (onComplete) {
    onComplete(id);
  }
  return MyApp.myQueue[id];
};
3
répondu Wasmoo 2015-08-19 20:43:44

essayez D'utiliser la promesse ECMA 6 ou n'importe quelle promesse du cadre js.Promiseest plus adapté à cette tâche. voir plus à https://developer.mozilla.org/

function MyApp() {}
module.exports = MyApp;

MyApp.myQueue = [];
MyApp.queueIsLocked = false;

MyApp.enqueue = function(item) {
 return new Promise(function(resolve, reject) {
      if (!MyApp.queueIsLocked) {
        MyApp.queueIsLocked = true;        
        MyApp.myQueue.push(item);
        MyApp.queueIsLocked = false;

        var onResolve = function() {
            console.log('item: ' + item.id);
            MyApp.queueIsLocked = true;   
            MyApp.findItemById(item.id).then(function(index){
                 if (index !== -1) {
                      MyApp.myQueue.splice(index, 1);
                      MyApp.queueIsLocked = false;
                      resolve(item.id);
                 }
            });     

        };

        item.send(onResolve,reject);
      }
  });
};

MyApp.findItemById = function(id) {
     return new Promise(function(resolve, reject) {
              var index = -1;
              if (MyApp.queueIsLocked) {
                setTimeout(function() {
                  // Attempt to find the index again.
                }, 100);
              } else {
                MyApp.queueIsLocked = true;
                for (var i=0; i<MyApp.myQueue.length; i++) {
                  if (MyApp.myQueue[i].id === id) {
                    index = i;
                    break;
                  }
                }
                resolve(index);
              }
        });
};
2
répondu Alex Nikulin 2015-08-27 07:41:03

déplacer MyApp.queueIsLocked = false; à la callback du serveur envoyer

0
répondu Prozi 2015-08-26 13:43:28