Потокобезопасность в JavaScript?

Мне назвали функцию, сохраняют (), эта функция собирает все исходные данные на странице и выполняет вызов Ajax к серверу для сохранения состояния работы пользователя.

сохраните (), в настоящее время называется, когда пользователь нажимает кнопку сохранения или выполняет некоторое другое действие, которое требует, чтобы у нас было актуальнейшее состояние на сервере (генерируйте документ от страницы, например).

Я добавляю в способности к автоматическому сохранению работу пользователя время от времени. Сначала я хотел бы предотвратить AutoSave, и сгенерированный Пользователь сохраняют от выполнения одновременно. Таким образом, у нас есть следующий код (я сокращаю большую часть кода, и это не 1:1, но должно быть достаточно для объяснения идеи):

var isSaving=false;
var timeoutId;
var timeoutInterval=300000;
function save(showMsg)
{
  //Don't save if we are already saving.
  if (isSaving)
  { 
     return;
  }
  isSaving=true;
  //disables the autoSave timer so if we are saving via some other method
  //we won't kick off the timer.
  disableAutoSave();

  if (showMsg) { //show a saving popup}
  params=CollectParams();
  PerformCallBack(params,endSave,endSaveError);

}
function endSave()
{  
    isSaving=false;
    //hides popup if it's visible

    //Turns auto saving back on so we save x milliseconds after the last save.
    enableAutoSave();

} 
function endSaveError()
{
   alert("Ooops");
   endSave();
}
function enableAutoSave()
{
    timeoutId=setTimeOut(function(){save(false);},timeoutInterval);
}
function disableAutoSave()
{
    cancelTimeOut(timeoutId);
}

Мой вопрос состоит в том, если этот код безопасен? Главные браузеры позволяют только единственному потоку выполняться за один раз?

Одна мысль, которую я имел, является этим, было бы хуже, чтобы пользователь нажал, сохраняют и не получают ответа, потому что мы сохраняемся автоматически (И я знаю, как изменить код для обработки этого). Кто-либо видит какие-либо другие проблемы здесь?

34
задан JoshBerke 12 February 2010 в 17:05
поделиться

5 ответов

JavaScript в браузерах является однопоточным. В любой момент времени вы будете выполнять только одну функцию. Функции завершатся до того, как будет введена следующая. Вы можете рассчитывать на такое поведение, поэтому, если вы используете свою функцию save () , вы никогда не войдете в нее снова, пока текущая функция не завершится.

Иногда это сбивает с толку (и все же остается правдой), когда у вас есть асинхронные запросы к серверу (или setTimeouts или setInterval), потому что тогда кажется, что ваши функции чередуются . Они не.

В вашем случае, хотя два вызова save () не будут перекрывать друг друга, ваше автосохранение и пользовательское сохранение могут выполняться последовательно.

Если вы просто хотите, чтобы сохранение происходило хотя бы раз в x секунд, вы можете сделать setInterval для своей функции сохранения и забыть об этом. Я не вижу необходимости во флаге isSaving .

Я думаю, что ваш код можно значительно упростить:

var intervalTime = 300000;
var intervalId = setInterval("save('my message')", intervalTime);
function save(showMsg)
{
  if (showMsg) { //show a saving popup}
  params=CollectParams();
  PerformCallBack(params, endSave, endSaveError);

  // You could even reset your interval now that you know we just saved.
  // Of course, you'll need to know it was a successful save.
  // Doing this will prevent the user clicking save only to have another
  // save bump them in the face right away because an interval comes up.
  clearInterval(intervalId);
  intervalId = setInterval("save('my message')", intervalTime);
}

function endSave()
{
    // no need for this method
    alert("I'm done saving!");
}

function endSaveError()
{
   alert("Ooops");
   endSave();
}
44
ответ дан 27 November 2019 в 16:53
поделиться

Все основные браузеры поддерживают только один поток JavaScript (если вы не используете веб-воркеров ) на странице.

Однако запросы XHR могут быть асинхронными. Но пока вы отключите возможность сохранения до тех пор, пока не вернется текущий запрос на сохранение, все должно работать нормально.

Мое единственное предложение - убедиться, что вы каким-то образом указываете пользователю, когда происходит автосохранение (отключите кнопку сохранения и т. Д.).

7
ответ дан 27 November 2019 в 16:53
поделиться

В настоящее время все основные браузеры выполняют javascript однопоточно (только не используйте web workers, так как несколько браузеров поддерживают эту технику!), так что этот подход безопасен.

Множество ссылок см. в Является ли JavaScript многопоточным?

.
2
ответ дан 27 November 2019 в 16:53
поделиться

По-моему, безопасно. Javascript является однопоточным (если вы не используете веб-работников)

Это не совсем по теме, но этот пост Джона Ресига охватывает потоки и таймеры javascript: http://ejohn.org/blog/how -javascript-timers-work /

2
ответ дан 27 November 2019 в 16:53
поделиться

Я думаю, что способ, который вы используете, лучше всего подходит для вашей ситуации. Используя флаг, вы гарантируете, что асинхронные вызовы не накладываются друг на друга. Мне тоже приходилось иметь дело с асинхронными вызовами на сервер, и я тоже использовал какой-то флаг для предотвращения наложения.

Как уже отмечали другие, JavaScript является однопоточным, но асинхронные вызовы могут быть сложными, если вы ожидаете, что во время пути к серверу все будет повторяться или не происходить.

Одно но: я не думаю, что вам нужно отключать автосохранение. Если автосохранение попытается произойти, когда пользователь сохраняет данные, то метод сохранения просто вернется, и ничего не произойдет. С другой стороны, вам не нужно отключать и снова включать автосохранение каждый раз, когда активируется автосохранение. Я бы рекомендовал перейти на setInterval и забыть об этом.

Кроме того, я сторонник минимизации глобальных переменных. Я бы, вероятно, рефакторил ваш код следующим образом:

var saveWork = (function() {
  var isSaving=false;
  var timeoutId;
  var timeoutInterval=300000;
  function endSave() {  
      isSaving=false;
      //hides popup if it's visible
  }
  function endSaveError() {
     alert("Ooops");
     endSave();
  }
  function _save(showMsg) {
    //Don't save if we are already saving.
    if (isSaving)
    { 
     return;
    }
    isSaving=true;

    if (showMsg) { //show a saving popup}
    params=CollectParams();
    PerformCallBack(params,endSave,endSaveError);
  }
  return {
    save: function(showMsg) { _save(showMsg); },
    enableAutoSave: function() {
      timeoutId=setInterval(function(){_save(false);},timeoutInterval);
    },
    disableAutoSave: function() {
      cancelTimeOut(timeoutId);
    }
  };
})();

Конечно, вы не обязаны его так рефакторить, но, как я уже сказал, мне нравится минимизировать глобальные переменные. Главное, чтобы все это работало без отключения и повторного включения автосохранения при каждом сохранении.

Edit: Забыл, что нужно создать приватную функцию сохранения, чтобы иметь возможность ссылаться на enableAutoSave

1
ответ дан 27 November 2019 в 16:53
поделиться
Другие вопросы по тегам:

Похожие вопросы: