Существуют ли какие-либо атомные javascript-операции для работы с асинхронной природой Ajax?

Я динамически загружаю код (функции) с сервера и выполняю его как код javascript, а затем сохраняю его в массиве и выполняю. Все эти фрагменты кода должны выполняться ровно один раз. Psuedocode следует как таковой

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

Проблема значительно сложнее, но по существу, если я выдаю команду ниже

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

все четыре будут исполнять if (foo in fooArray) и предположить, что он не находится в массиве, и все четыре будут приступать к извлечению кода и его выполнению. Я помню, как в тот же день я узнал о семафорах и мьютексах, есть ли такие вещи для javascript.

Ответ 1

JavaScript - хороший язык, который отлично работает с асинхронными обратными вызовами, тайм-аутами, интервалами и пользовательскими событиями, но не имея проблем с concurrency. Это возможно, потому что JavaScript по сути однопоточен - данный кусок кода всегда выполняется атомарно и никогда не прерывается другим потоком, выполняющим JavaScript.

Ваша функция fetch() всегда будет выполняться без прерывания. Если он выполняется как часть обратного вызова AJAX и если несколько обратных вызовов AJAX находятся на ожидании, они будут поставлены в очередь.

Другой пример: если у вас есть обработчик событий, назначенный входному элементу, и вы запускаете событие несколько раз подряд, обработчики событий не будут выполняться одновременно. Вместо этого они будут поставлены в очередь и выполняться последовательно. Это также относится к нескольким событиям, вызванным setTimeout()/setInterval().

В качестве примечания: это одна из причин, почему node.js является настолько надежным: он использует только один поток и никогда не блокирует операции ввода /O, но использует обратные вызовы, когда данные готовы/событие происходит.

Ответ 2

Javascript по существу однопоточный, поэтому вам не нужен мьютекс. Ваша выборка могла бы установить флаги так, чтобы последующие вызовы выборки могли избежать вызова ajax, например:

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