Безопасность потоков в Javascript?
У меня есть функция, называемая save(), эта функция собирает все входы на странице и выполняет вызов AJAX на сервер, чтобы сохранить состояние работы пользователя.
save() в настоящее время вызывается, когда пользователь нажимает кнопку сохранения или выполняет другое действие, которое требует от нас самого последнего состояния на сервере (например, сгенерировать документ со страницы).
Я добавляю возможность автоматического сохранения работы пользователя так часто. Сначала я хотел бы предотвратить автоматическое сохранение Автосохранения и сгенерированного пользователем сохранения. Таким образом, у нас есть следующий код (я сокращаю большую часть кода, и это не 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 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);
}
Мой вопрос в том, безопасен ли этот код? Могут ли основные браузеры одновременно выполнять только один поток?
Одна мысль, что у меня было, было бы хуже, если бы пользователь нажал кнопку "Сохранить" и не получил ответа, потому что мы автосохраняемся (и я знаю, как изменить код, чтобы справиться с этим). Кто-нибудь видит другие проблемы здесь?
Ответы
Ответ 1
JavaScript в браузерах однопоточный. Вы всегда будете в одной функции в любой момент времени. Функции будут выполнены до следующего ввода. Вы можете рассчитывать на это поведение, поэтому, если вы находитесь в своей функции save()
, вы никогда не введете его снова, пока не закончите текущий.
Если это иногда путается (и все же остается верным), когда у вас есть асинхронные запросы сервера (или setTimeouts или setIntervals), потому что тогда кажется, что ваши функции чередование. Это не так.
В вашем случае, в то время как два вызова 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();
}
Ответ 2
Все основные браузеры поддерживают только один поток javascript (если вы не используете веб-работников) на странице.
XHR-запросы могут быть асинхронными. Но до тех пор, пока вы отключите возможность сохранения до тех пор, пока текущий запрос не сохранит возврат, все должно работать нормально.
Мое единственное предложение - убедиться, что вы как-то указываете пользователю, когда возникает автосохранение (отключите кнопку сохранения и т.д.).
Ответ 3
Все основные браузеры в настоящее время выполняют однопоточное выполнение javascript (просто не используйте веб-работников, поскольку несколько браузеров поддерживают эту технику!), поэтому этот подход безопасен.
Для получения нескольких ссылок см. Является ли JavaScript многопоточным?
Ответ 4
Мне кажется безопасным. Javascript является однопоточным (если вы не используете веб-мастеров)
Это не совсем по теме, но этот пост Джона Ресига охватывает потоки и таймеры javascript:
http://ejohn.org/blog/how-javascript-timers-work/
Ответ 5
Я думаю, что то, как вы справляетесь с этим, лучше всего подходит для вашей ситуации. Используя флаг, вы гарантируете, что асинхронные вызовы не перекрываются. Мне также приходилось иметь дело с асинхронными вызовами на сервере, а также использовать какой-то флаг для предотвращения перекрытия.
Как уже указывалось, JavaScript является однопоточным, но асинхронные вызовы могут быть сложными, если вы ожидаете, что что-то скажет то же самое или не произойдет во время кругосветного путешествия на сервер.
Одна вещь, однако, заключается в том, что я не думаю, что вам действительно нужно отключить автоматическое сохранение. Если автоматическое сохранение будет происходить при сохранении пользователем, метод сохранения просто вернется и ничего не произойдет. С другой стороны, вы автоматически отключите и повторно активируете автосохранение при каждом включении автосохранения. Я бы рекомендовал изменить на setInterval, а затем забыть об этом.
Кроме того, я сторонник минимизации глобальных переменных. Я бы, вероятно, реорганизовал ваш код следующим образом:
var saveWork = (function() {
var isSaving=false;
var timeoutId;
var timeoutInterval=300000;
function endSave() {
isSaving=false;
//hides popup if it 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);
}
};
})();
Вам не нужно реорганизовывать это, конечно, но, как я уже сказал, мне нравится минимизировать глобальные переменные. Важно то, что все это должно работать без отключения и повторного включения автоматического сохранения при каждом сохранении.
Изменить: Забытый должен был создать частную функцию сохранения, чтобы иметь возможность ссылаться на enableAutoSave