Як реалізувати блокування в JavaScript


83

Як можна lockв JavaScript реалізувати щось еквівалентне в C #?

Отже, щоб пояснити, на що я думаю, простий варіант використання:

Користувач натискає кнопку B. Bвикликає подію onclick. Якщо Bє у event-stateвипадку, чекає, поки Bвін буде ready-stateперед розмноженням. Якщо Bввімкнено ready-state, Bзаблоковано та встановлено значення event-state, тоді подія поширюється. Коли розповсюдження події завершено, Bвстановлюється значення ready-state.

Я міг зрозуміти, як можна зробити щось наближене до цього, просто додавши та видаливши клас ready-stateіз кнопки. Однак проблема полягає в тому, що користувач може натиснути кнопку двічі поспіль швидше, ніж можна встановити змінну, тому ця спроба блокування в деяких випадках не вдасться.

Хтось знає, як реалізувати блокування, яке не дасть збою в JavaScript?


1
Будемо вдячні за відповіді, які враховують паралельне виконання JS (тобто, як у IE9).
OrangeDog

@smart Отже, ви хочете тимчасово призупинити розповсюдження поточної події до завершення розповсюдження попередньої події. Я не думаю, що цього можна зробити. Ви можете зробити це: відкинути подію, а потім запустити іншу, коли попередня подія закінчиться розповсюджуватися.
Šime Vidas

@OrangeDog - Я лише чув, як IE9 намагається використовувати виділене ядро ​​для компіляції, нічого про паралельне виконання, чи можете ви цитувати джерело?
Брендон

1
@Brandon - Це може означати, як ви припускаєте, паралельно рендерингу, а не паралельно самому собі.
OrangeDog

Відповіді:


85

Блокування - сумнівна ідея в JS, яка має бути без потоків і не потребує захисту паралельності. Ви хочете поєднати дзвінки при відкладеному виконанні. Я використовую для цього схему зворотних дзвінків. Щось на зразок цього:

var functionLock = false;
var functionCallbacks = [];
var lockingFunction = function (callback) {
    if (functionLock) {
        functionCallbacks.push(callback);
    } else {
        $.longRunning(function(response) {
             while(functionCallbacks.length){
                 var thisCallback = functionCallbacks.pop();
                 thisCallback(response);
             }
        });
    }
}

Ви також можете реалізувати це за допомогою прослуховувачів подій DOM або рішення pubsub.


18
чи не могли б ви надати довідкову документацію, що обґрунтовує вашу заяву про те, що "JS призначений для роботи без потоків і не потребує одночасного захисту"? Я хотів би прочитати більше про це.
smartcaveman

2
+1 - Беручи до уваги, що Node.js (веб-сервер JavaScript) був побудований для того, щоб скористатися перевагами одиночного потоку та механізму зворотного виклику, я б погодився, що вам не потрібно турбуватися про блокування властивості, оскільки не буде жодної гонки.
Фентон,

4
З якої бібліотеки $ .longRunning? Це не в (поточному) jQuery.
Quantum7,

3
коли слід встановити functionLock?
Елтон Гарсія де Сантана,

9
"JS, який має бути без потоків і не потребує одночасності", сумнівний. JS не використовує попереджувальну паралельність, але дозволяє використовувати кооперативну паралельність (зворотний виклик або асинхронізація / очікування). Ви, безумовно, можете мати умови перегонів при їх використанні, і, можливо, захочете використовувати абстракцію мьютексу, щоб уникнути їх.
ysdx

32

JavaScript, за дуже невеликими винятками ( XMLHttpRequest onreadystatechangeобробники в деяких версіях Firefox), є одночасним циклом подій . Тому вам не потрібно турбуватися про блокування в цьому випадку.

JavaScript має паралельну модель, засновану на "циклі подій". Ця модель значно відрізняється від моделі інших мов, таких як C або Java.

...

Час виконання JavaScript містить чергу повідомлень, яка являє собою список повідомлень, що підлягають обробці. До кожного повідомлення приєднана функція. Коли стек порожній, повідомлення виймається з черги та обробляється.Обробка складається із виклику пов'язаної функції (і, таким чином, створення початкового кадру стека) Обробка повідомлень закінчується, коли стек знову стає порожнім.

...

Кожне повідомлення обробляється повністю перед будь-яким іншим повідомленням. Це пропонує кілька приємних властивостей під час міркувань щодо вашої програми, включаючи той факт, що всякий раз, коли функція запускається, вона не може бути попередньо випущена і працюватиме повністю до запуску будь-якого іншого коду (і може змінювати дані, якими функція маніпулює). Наприклад, це відрізняється від C, де, якщо функція працює в потоці, її можна зупинити в будь-який момент для запуску іншого коду в іншому потоці.

Недоліком цієї моделі є те, що якщо надсилання повідомлення триває занадто довго, веб-програма не може обробляти взаємодії користувачів, такі як клацання або прокрутка. Браузер пом'якшує це за допомогою діалогового вікна "сценарій займає занадто багато часу". Хороша практика, яку слід дотримуватися, - це скоротити обробку повідомлень і, якщо це можливо, розрізати одне повідомлення на кілька повідомлень.

Щоб отримати додаткові посилання на паралельність циклу подій, див. E


А з веб-робітниками (не UI-потоками) у javascript, кожен об'єкт клонується перед надсиланням іншому робочому (потоку) або потоку інтерфейсу, тоді об'єкт має різні екземпляри в різних потоках, і кожен потік має цикл подій і, нарешті, я погоджуюся з відповіддю Майка як корисна відповідь. Дякую Майку.
Ехсан Мохаммаді,

10

У мене був успіх mutex-обіцянка .

Я погоджуюсь з іншими відповідями, що у вашому випадку вам може не знадобитися блокування. Але це неправда, що ніколи не потрібно блокувати Javascript. Вам потрібна взаємна ексклюзивність при доступі до зовнішніх ресурсів, які не обробляють паралельність.


Спасибі дуже потрібна річ.
Дмитро

6

Замки - це концепція, необхідна в багатопотоковій системі. Навіть із робочими потоками повідомлення надсилаються між робочими за значенням, тому блокування не потрібно.

Я підозрюю, що вам потрібно просто встановити семафор (систему позначення) між кнопками.


1
чи є у вас такі приклади чи ресурси для "системи семафору / позначення", реалізованої в JavaScript?
smartcaveman

4
PLENT прямо тут на SO, тобто stackoverflow.com/questions/4194346/…
James Westgate

Джеймс У посиланні, яке ви розмістили, я не бачу прикладів семафору. Мені потрібно запобігти викликам на зовнішній сервер, оскільки вони дозволяють лише один запит на секунду, тому мені потрібен приклад позначення, який запобігає виклику, поки не пройде час, який минув всередині мого наступного циклу, не пропускаючи запит для кожного з циклів.
Клаус,

Привіт @ Клаусе. Дві речі поширюються на думку, залежно від вашого стеку, ви можете просто скористатися такою бібліотекою, як Axios, яка, схоже, підтримує плагін обмеження швидкості, або, можливо, ви можете створити веб-співробітника, який обмежує запити на основі домену.
Джеймс Вестгейт,

Дякую за відповідь. Я вирішив це, викликавши подію кліку в кінці обробки в поєднанні з setInterval. Встановивши інтервал 1000 мілісекунд, я тепер підкоряюся обмеженню хост-сервера на запити, і він більше не виходить із виклику Ajax, перш ніж відповідь буде повернуто.
Клаус

2

Чому б вам не вимкнути кнопку та не ввімкнути її після завершення події?

<input type="button" id="xx" onclick="checkEnableSubmit('true');yourFunction();">

<script type="text/javascript">

function checkEnableSubmit(status) {  
  document.getElementById("xx").disabled = status;
}

function yourFunction(){

//add your functionality

checkEnableSubmit('false');
}

</script>

Щасливого кодування !!!


0

Якесь доповнення до відповіді JoshRiver відповідно до мого випадку;

var functionCallbacks = [];
    var functionLock = false;
    var getData = function (url, callback) {
                   if (functionLock) {
                        functionCallbacks.push(callback);
                   } else {
                       functionLock = true;
                       functionCallbacks.push(callback);
                        $.getJSON(url, function (data) {
                            while (functionCallbacks.length) {
                                var thisCallback = functionCallbacks.pop();
                                thisCallback(data);
                            }
                            functionLock = false;
                        });
                    }
                };

// Usage
getData("api/orders",function(data){
    barChart(data);
});
getData("api/orders",function(data){
  lineChart(data);
});

Буде лише один виклик api, і ці дві функції матимуть однаковий результат.


0

Замки все ще використовуються в JS. З мого досвіду мені потрібно було лише використовувати блокування, щоб запобігти клацанню спаму на елементи, що здійснюють виклики AJAX. Якщо у вас встановлений завантажувач для викликів AJAX, це не потрібно (як і вимкнення кнопки після натискання). Але в будь-якому випадку ось те, що я використовував для блокування:

var LOCK_INDEX = [];
function LockCallback(key, action, manual) {
    if (LOCK_INDEX[key])
        return;
    LOCK_INDEX[key] = true;
    action(function () { delete LOCK_INDEX[key] });
    if (!manual)
        delete LOCK_INDEX[key];
}

Використання:

Розблокування вручну (зазвичай для XHR)

LockCallback('someKey',(delCallback) => { 
    //do stuff
    delCallback(); //Unlock method
}, true)

Автоматичне розблокування

LockCallback('someKey',() => { 
    //do stuff
})

0

Ось простий механізм блокування, реалізований за допомогою закриття

const createLock = () => {

    let lockStatus = false

    const release = () => {
        lockStatus = false
    }

    const acuire = () => {
        if (lockStatus == true)
            return false
        lockStatus = true
        return true
    }
    
    return {
        lockStatus: lockStatus, 
        acuire: acuire,
        release: release,
    }
}

lock = createLock() // create a lock
lock.acuire() // acuired a lock

if (lock.acuire()){
  console.log("Was able to acuire");
} else {
  console.log("Was not to acuire"); // This will execute
}

lock.release() // now the lock is released

if(lock.acuire()){
  console.log("Was able to acuire"); // This will execute
} else {
  console.log("Was not to acuire"); 
}

lock.release() // Hey don't forget to release

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.