Cách triển khai khóa JavaScript


83

Làm thế nào một cái gì đó có thể tương đương với lock C # có thể được triển khai trong JavaScript?

Vì vậy, để giải thích những gì tôi đang nghĩ, một trường hợp sử dụng đơn giản là:

Người dùng nhấp vào nút B. Btăng một sự kiện onclick. Nếu Blà trong event-statesự kiện, hãy đợi Bở trong ready-statetrước khi tuyên truyền. Nếu Bở trong ready-state, Bbị khóa và được đặt thành event-state, thì sự kiện sẽ lan truyền. Khi quá trình truyền của sự kiện hoàn tất, Bđược đặt thành ready-state.

Tôi có thể thấy điều gì đó gần với điều này có thể được thực hiện như thế nào, chỉ đơn giản bằng cách thêm và xóa lớp ready-statekhỏi nút. Tuy nhiên, vấn đề là người dùng có thể nhấp vào một nút hai lần liên tiếp nhanh hơn so với biến có thể được đặt, vì vậy nỗ lực khóa này sẽ không thành công trong một số trường hợp.

Có ai biết cách triển khai một khóa không bị lỗi trong JavaScript không?


1
Các câu trả lời có tính đến việc thực thi JS song song (ví dụ như trong IE9) sẽ được đánh giá cao.
OrangeDog

@smart Vì vậy, bạn muốn tạm dừng việc truyền sự kiện hiện tại cho đến khi quá trình truyền sự kiện trước đó hoàn tất. Tôi không nghĩ rằng điều đó có thể được thực hiện. Bạn có thể làm điều này: loại bỏ sự kiện và sau đó kích hoạt một sự kiện khác khi sự kiện trước đó đã kết thúc quá trình lan truyền.
Šime Vidas

@OrangeDog - Tôi chỉ nghe nói IE9 cố gắng sử dụng một lõi chuyên dụng để biên dịch, không có gì về thực thi song song, bạn có thể trích dẫn nguồn không?
Brandon

1
@Brandon - Như bạn đề xuất, điều đó có nghĩa là song song với trình kết xuất, thay vì song song với chính nó.
OrangeDog

Câu trả lời:


84

Khóa là một ý tưởng đáng nghi vấn trong JS, được thiết kế là không có luồng và không cần bảo vệ đồng thời. Bạn đang tìm cách kết hợp các cuộc gọi khi thực hiện hoãn lại. Mô hình tôi làm theo cho việc này là sử dụng các lệnh gọi lại. Một cái gì đó như thế này:

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);
             }
        });
    }
}

Bạn cũng có thể triển khai điều này bằng cách sử dụng trình nghe sự kiện DOM hoặc giải pháp pubsub.


18
bạn có thể vui lòng cung cấp tài liệu tham khảo giải thích cho tuyên bố của bạn rằng "JS được dự định là không có luồng và không cần bảo vệ đồng thời" không? Tôi muốn đọc thêm về điều này.
nhặt rác thông minh

2
+1 - Cho rằng Node.js (máy chủ web JavaScript) được xây dựng để tận dụng cơ chế gọi lại và luồng đơn lẻ của JavaScript, tôi đồng ý rằng bạn không cần lo lắng về việc khóa thuộc tính vì sẽ không có điều kiện chủng tộc.
Fenton

4
$ .LongRunning từ thư viện nào? Nó không có trong jQuery (hiện tại).
Quantum7

3
khi functionLock được thiết lập?
Elton Garcia de Santana

9
"JS được dự định là không có luồng và không cần đồng thời" là không rõ ràng. JS không sử dụng đồng thời phủ đầu nhưng cho phép bạn sử dụng đồng thời hợp tác (dựa trên callback hoặc async / await). Bạn chắc chắn có thể có các điều kiện về chủng tộc khi sử dụng những điều kiện đó và có thể muốn sử dụng trừu tượng mutex để tránh chúng.
ysdx 14/02/18

32

JavaScript, với một số rất ít ngoại lệ ( XMLHttpRequest onreadystatechangetrình xử lý trong một số phiên bản của Firefox) đồng thời vòng lặp sự kiện . Vì vậy, bạn không cần phải lo lắng về việc khóa trong trường hợp này.

JavaScript có một mô hình đồng thời dựa trên một "vòng lặp sự kiện". Mô hình này khá khác so với mô hình trong các ngôn ngữ khác như C hoặc Java.

...

Thời gian chạy JavaScript chứa hàng đợi thông báo, là danh sách các thông báo sẽ được xử lý. Mỗi tin nhắn được liên kết với một chức năng. Khi ngăn xếp trống, một thông báo được đưa ra khỏi hàng đợi và được xử lý.Quá trình xử lý bao gồm việc gọi hàm liên kết (và do đó tạo ra một khung ngăn xếp ban đầu) Quá trình xử lý thông báo kết thúc khi ngăn xếp trống trở lại.

...

Mỗi thư được xử lý hoàn toàn trước khi bất kỳ thư nào khác được xử lý. Điều này cung cấp một số thuộc tính tốt khi lý luận về chương trình của bạn, bao gồm thực tế là bất cứ khi nào một hàm chạy, nó không thể được xóa trước và sẽ chạy hoàn toàn trước khi bất kỳ mã nào khác chạy (và có thể sửa đổi dữ liệu mà hàm thao tác). Điều này khác với C, chẳng hạn, trong đó nếu một hàm chạy trong một luồng, thì nó có thể bị dừng tại bất kỳ thời điểm nào để chạy một số mã khác trong một luồng khác.

Nhược điểm của mô hình này là nếu một thông báo mất quá nhiều thời gian để hoàn thành, ứng dụng web không thể xử lý các tương tác của người dùng như nhấp hoặc cuộn. Trình duyệt giảm thiểu điều này bằng hộp thoại "tập lệnh mất quá nhiều thời gian để chạy". Một phương pháp hay để làm theo là làm cho quá trình xử lý tin nhắn ngắn gọn và nếu có thể, hãy cắt một tin nhắn thành nhiều tin nhắn.

Để biết thêm liên kết về đồng thời vòng lặp sự kiện, hãy xem E


Và với Web worker (luồng không phải UI) trong javascript, mỗi đối tượng sao chép trước khi gửi đến một worker (luồng) hoặc luồng UI khác sau đó đối tượng có các phiên bản khác nhau trong các luồng khác nhau và mỗi luồng có vòng lặp sự kiện sở hữu và cuối cùng tôi đồng ý với câu trả lời của Mike là một câu trả lời hữu ích. Cảm ơn Mike.
Ehsan Mohammadi

10

Tôi đã thành công mutex-hứa .

Tôi đồng ý với các câu trả lời khác mà bạn có thể không cần khóa trong trường hợp của mình. Nhưng không đúng là không bao giờ cần khóa trong Javascript. Bạn cần độc quyền lẫn nhau khi truy cập các tài nguyên bên ngoài không xử lý đồng thời.


Cảm ơn điều rất cần thiết.
Dmitrij Polyanin

6

Khóa là một khái niệm bắt buộc trong một hệ thống đa luồng. Ngay cả với các chuỗi worker, các tin nhắn được gửi theo giá trị giữa các worker vì vậy việc khóa là không cần thiết.

Tôi nghi ngờ rằng bạn chỉ cần đặt một semaphore (hệ thống gắn cờ) giữa các nút của bạn.


1
bạn có bất kỳ ví dụ hoặc tài nguyên nào như vậy cho 'hệ thống gắn cờ / semaphore' được triển khai bằng JavaScript không?
nhặt rác thông minh

4
PLenty ngay tại đây trên SO tức là stackoverflow.com/questions/4194346/…
James Westgate

James Tôi không thấy bất kỳ ví dụ semaphore nào trong liên kết bạn đã đăng. Tôi cần ngăn các cuộc gọi đến máy chủ bên ngoài vì chúng chỉ cho phép một yêu cầu mỗi giây, vì vậy tôi cần một ví dụ gắn cờ sẽ ngăn cuộc gọi cho đến khi thời gian trôi qua trôi qua bên trong vòng lặp for next của tôi mà không bỏ qua yêu cầu cho mỗi vòng lặp.
Claus

Chào bạn @Claus. Có hai điều cần lưu ý, tùy thuộc vào ngăn xếp của bạn, bạn có thể chỉ cần sử dụng một thư viện như Axios, có vẻ như hỗ trợ plugin giới hạn tỷ lệ hoặc có lẽ bạn có thể tạo một webworker giới hạn tỷ lệ yêu cầu dựa trên miền.
James Westgate

Cảm ơn vì sự trả lời. Tôi đã giải quyết nó bằng cách gọi sự kiện nhấp chuột khi kết thúc quá trình kết hợp với setInterval. Đặt khoảng thời gian thành 1000 mili giây, tôi hiện tuân theo giới hạn máy chủ lưu trữ đối với các yêu cầu và nó không còn thoát lệnh gọi Ajax trước khi phản hồi được trả lại.
Claus

2

Tại sao bạn không tắt và bật nó sau khi kết thúc sự kiện?

<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>

Chúc bạn viết mã vui vẻ !!!


0

Một số bổ sung cho câu trả lời của JoshRiver theo trường hợp của tôi;

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);
});

Sẽ chỉ có một lệnh gọi api và hai hàm này sẽ sử dụng cùng một kết quả.


0

Các khóa vẫn có công dụng trong JS. Theo kinh nghiệm của tôi, tôi chỉ cần sử dụng khóa để ngăn spam nhấp vào các phần tử thực hiện cuộc gọi AJAX. Nếu bạn đã thiết lập bộ tải cho các cuộc gọi AJAX thì điều này không bắt buộc (cũng như vô hiệu hóa nút sau khi nhấp). Nhưng một trong hai cách ở đây là những gì tôi đã sử dụng để khóa:

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];
}

Sử dụng:

Mở khóa bằng tay (thường dành cho XHR)

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

Mở khóa tự động

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

0

Đây là một cơ chế khóa đơn giản, được thực hiện thông qua đóng

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

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.