Безопасность потоков в 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'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);
}
Мой вопрос, если этот код безопасен? Разрешают ли основные браузеры одновременно выполнять только один поток?
Одна мысль, которая у меня возникла, заключается в том, что пользователю будет хуже нажать кнопку "Сохранить" и не получить ответ, потому что мы автосохраняем (и я знаю, как изменить код, чтобы справиться с этим). Кто-нибудь видит какие-либо другие проблемы здесь?
5 ответов
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();
}
Все основные браузеры поддерживают только одну нить JavaScript (если вы не используете веб-работников) на странице.
Однако запросы XHR могут быть асинхронными. Но пока вы отключаете возможность сохранения до тех пор, пока не вернется текущий запрос на сохранение, все должно работать нормально.
Мое единственное предложение - убедиться, что вы как-то указываете пользователю, когда происходит автосохранение (отключите кнопку сохранения и т. Д.).
Все основные браузеры в настоящее время выполняют однопоточное выполнение JavaScript (просто не используйте веб-работников, поскольку некоторые браузеры поддерживают эту технику!), Поэтому этот подход безопасен.
Несколько ссылок см. В разделе " Многопоточность JavaScript"?
Выглядит безопасно для меня. Javascript является однопоточным (если вы не используете веб-работников)
Это не совсем по теме, но этот пост Джона Резига охватывает потоки JavaScript и таймеры: http://ejohn.org/blog/how-javascript-timers-work/
Я думаю, то, как вы справляетесь с этим, лучше всего подходит для вашей ситуации. Используя этот флаг, вы гарантируете, что асинхронные вызовы не перекрываются. Я также имел дело с асинхронными вызовами к серверу, а также использовал какой-то флаг, чтобы предотвратить наложение.
Как уже отмечали другие, 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);
}
};
})();
Конечно, вам не нужно это так рефакторировать, но, как я уже сказал, мне нравится минимизировать глобальные переменные. Важно то, что все это должно работать без отключения и повторного включения автосохранения при каждом сохранении.
Изменить: Забыл, чтобы создать частную функцию сохранения, чтобы иметь возможность ссылаться из enableAutoSave