JavaScriptでロックを実装する方法


83

lockC#と同等のものをJavaScriptでどのように実装できますか?

したがって、私が考えている簡単なユースケースを説明すると、次のようになります。

ユーザーがボタンをクリックしますBBonclickイベントを発生させます。がイベントにある場合Bは、伝播する前にが存在するのをevent-state待ちます。場合であり、ロックされ、に設定され、その後、イベントが伝搬します。イベントの伝播が完了すると、はに設定されます。Bready-stateBready-stateBevent-stateBready-state

ready-stateボタンにクラスを追加したり、ボタンからクラスを削除したりするだけで、これに近いことがどのように行われるかがわかりました。ただし、問題は、ユーザーが変数を設定するよりも速くボタンを2回続けてクリックできるため、状況によってはこのロックの試行が失敗することです。

JavaScriptで失敗しないロックを実装する方法を知っている人はいますか?


1
JSの並列実行(IE9のように)を考慮した回答をいただければ幸いです。
OrangeDog 2011年

@smartしたがって、前のイベントの伝播が完了するまで、現在のイベントの伝播を一時的に一時停止する必要があります。それができるとは思いません。これを行うことができます:イベントを破棄し、前のイベントの伝播が終了したときに別のイベントを発生させます。
–ŠimeVidas 2011

@ OrangeDog-IE9がコンパイルに専用のコアを使用しようとしていると聞いただけで、並列実行については何も聞いていません。ソースを引用できますか?
ブランドン

1
@ Brandon-それは、あなたが示唆しているように、それ自体に平行ではなく、レンダラーに平行であることを意味するかもしれません。
OrangeDog 2011年

回答:


84

ロックは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 2011年

2
+ 1-Node.js(JavaScript Webサーバー)がJavaScriptのローンスレッドとコールバックメカニズムを利用するように構築されていることを考えると、競合状態が発生しないため、プロパティのロックについて心配する必要はないことに同意します。
フェントン2011年

4
$ .longRunningはどのライブラリからのものですか?(現在の)jQueryにはありません。
Quantum7 2017年

3
functionLockが設定されるのはいつですか?
エルトンガルシアデサンタナ2017年

9
「スレッドレスで並行性を必要としないことを目的としたJS」は疑わしいです。JSはプリエンプティブ同時実行を使用しませんが、協調同時実行(コールバックまたは非同期/待機ベース)を使用できます。それらを使用するときは間違いなく競合状態になる可能性があり、それらを回避するためにミューテックス抽象化を使用することをお勧めします。
ysdx 2018

32

JavaScriptは、ごくわずかな例外XMLHttpRequest onreadystatechangeFirefoxの一部のバージョンではハンドラー)を除いて、イベントループ並行です。したがって、この場合はロックについて心配する必要はありません。

JavaScriptには、「イベントループ」に基づく並行性モデルがあります。このモデルは、CやJavaなどの他の言語のモデルとはかなり異なります。

..。

JavaScriptランタイムには、処理されるメッセージのリストであるメッセージキューが含まれています。各メッセージには関数が関連付けられています。スタックが空になると、メッセージがキューから取り出されて処理されます。処理は、関連する関数の呼び出し(したがって、初期スタックフレームの作成)で構成されます。スタックが再び空になると、メッセージ処理は終了します。

..。

各メッセージは、他のメッセージが処理される前に完全に処理されます。これは、関数が実行されるたびにプリエンプトできず、他のコードが実行される前に完全に実行される(そして関数が操作するデータを変更できる)という事実を含め、プログラムについて推論するときにいくつかの優れたプロパティを提供します。これはCとは異なります。たとえば、関数がスレッドで実行されている場合、関数をいつでも停止して、別のスレッドで他のコードを実行できます。

このモデルの欠点は、メッセージの完了に時間がかかりすぎると、Webアプリケーションがクリックやスクロールなどのユーザー操作を処理できないことです。ブラウザは、「スクリプトの実行に時間がかかりすぎる」ダイアログでこれを軽減します。従うべき良い習慣は、メッセージ処理を短くし、可能であれば1つのメッセージを複数のメッセージに分割することです。

イベントループの同時実行に関するその他のリンクについては、Eを参照してください。


そして、JavaScriptのWebワーカー(非UIスレッド)では、各オブジェクトは別のワーカー(スレッド)またはUIスレッドに送信する前にクローンを作成し、オブジェクトは異なるスレッドで異なるインスタンスを持ち、各スレッドはイベントループを所有し、最後にマイクの答えに同意します。役立つ答え。マイクに感謝します。
EhsanMohammadi19年

10

mutex-promiseで成功しました。

私はあなたがあなたのケースでロックする必要がないかもしれないという他の答えに同意します。しかし、Javascriptでロックする必要がないというのは真実ではありません。並行性を処理しない外部リソースにアクセスする場合は、相互排他性が必要です。


非常に必要なものに感謝します。
DmitrijPolyanin19年

6

ロックは、マルチスレッドシステムで必要な概念です。ワーカースレッドを使用する場合でも、メッセージはワーカー間で値によって送信されるため、ロックは不要です。

ボタンの間にセマフォ(フラグシステム)を設定するだけでよいのではないかと思います。


1
JavaScriptで実装された「セマフォ/フラグシステム」のそのような例やリソースはありますか?
smartcaveman 2011年


ジェームズあなたが投稿したリンクにセマフォの例はありません。外部サーバーへの呼び出しは1秒に1つの要求しか許可しないため、これを防ぐ必要があります。そのため、各ループの要求をスキップせずに、次のループ内で経過時間が経過するまで呼び出しを防ぐフラグの例が必要です。
クラウス

こんにちは@Claus。スタックに応じて、レート制限プラグインをサポートしているように見えるAxiosのようなライブラリを使用するか、ドメインに基づいてリクエストをレート制限するWebworkerを作成することができます。
James Westgate

返信ありがとうございます。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呼び出しは1つだけで、これら2つの関数は同じ結果を消費します。


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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.