2011-09-01 5 views
10

Sto caricando dinamicamente il codice (funzioni) da un server ed eseguendolo come codice javascript, quindi memorizzandolo in un array ed eseguendolo. Tutti questi snippet di codice devono essere eseguiti esattamente una volta. Il psuedocodarlo segue come taleEsistono operazioni javascript atomiche per gestire la natura asincrona di Ajax?

function fetch(foo){ 
    if (foo in fooArray){ 
      //Do Nothing 
    else{ 
      //Fetch foo via Ajax and execute foo() 
    } 
} 

il problema è molto più complessa, ma essenzialmente se usa il comando sotto

fetch('someFunctionName'); 
fetch('someFunctionName'); 
fetch('someFunctionName'); 
fetch('someFunctionName'); 

tutti e quattro eseguirà la if (foo in fooArray) ed assumere che non è nella matrice, e tutti e quattro procederanno a recuperare il codice ed eseguirlo. Ricordo di nuovo durante la giornata di apprendimento di semafori e mutex, ci sono cose del genere per javascript.

+1

quanto pare è possibile, dal momento che jQuery fa questo: http://stackoverflow.com/questions/7131991/asynchronous-and- synchronous-terms – Mchl

+0

Ho scritto un blog a riguardo [Perché nessuno strumento di concorrenza in javascript] (http://uzairfarooq.github.io/why-no-concurrency-control-tool-in-javascript/) –

+1

il collegamento è broken, dovrebbe essere: http://blog.uzairfarooq.com/why-no-concurrency-control-tool-in-javascript –

risposta

30

JavaScript è un linguaggio piacevole che funziona perfettamente con callback asincroni, timeout, intervalli e eventi utente, senza tuttavia avere problemi di concorrenza. Questo è possibile perché JavaScript è essenzialmente single-threaded: una determinata parte di codice viene eseguita sempre atomicamente e mai interrotta da un altro thread che esegue JavaScript.

La tua funzione fetch() verrà sempre eseguita senza alcuna interruzione. Se è eseguito come parte del callback AJAX e se più callback AJAX sono in sospeso, verranno messi in coda.

Un altro esempio: se si ha un gestore di eventi assegnato a un elemento di input e si attiva l'evento più volte contemporaneamente, i gestori di eventi non verranno eseguiti contemporaneamente. Invece saranno accodati ed eseguiti sequenzialmente. Questo vale anche per più eventi attivati ​​da setTimeout()/setInterval().

Come nota a margine: questo è uno dei motivi per cui node.js è così robusto: utilizza solo un singolo thread e non si blocca mai sull'I/O ma utilizza i callback quando i dati sono pronti/l'evento si verifica.

+5

Strettamente parlando [web worker] (http://en.wikipedia.org/wiki/Web_Workers) consente l'esecuzione simultanea di JS, ma poiché l'unica interazione tra i web worker e il JS "normale" avviene tramite i messaggi tu * still * non hai problemi di concorrenza. –

+0

Tomasz Vedo quello che stai dicendo, grazie. – puk

+0

@Tomasz, se le chiamate AJAX impiegano troppo tempo, forse uno potrebbe non voler avere troppe (più di 2 o 6 a seconda del browser) in sospeso, quindi offro una soluzione di caching di seguito .. cosa ne pensi? –

1

Javascript è essenzialmente single-threaded quindi non è necessario un mutex. Il tuo prendere potrebbe impostare flag in modo che le successive chiamate fetch potevano evitare di effettuare chiamate Ajax ad esempio:

var beingFetched = {};//map onflight -> callbacks 
function fetch(foo){ 
    if (foo in fooArray){ 
     //Do Nothing 
    } else { 
     if (beingFetched.foo) { //note empty array is truthy 
      //register a callback 
      var callback = function(r){ 
      //anything you need to do wit the return object r 
      //maybe even eval it. 
      }; 
      //the callback would more likely be an argument to fetch itself 
      //or you could use a promise API instead so that you can at your will 
      //register multiple callbacks - for error, for success etc. 
      beingFetched.foo.push(callback); 
     } else { 
      beingFetched.foo = [];//truthy 
      //Fetch foo via Ajax and execute 
      $.ajax("getFoo/"+foo).done(function() { 
       _.each(beingFetched.foo, function(cb){ 
        cb.apply(cb,arguments); 
       }); 
       delete beingFetched.foo; 
      }); 
     } 
    } 
}