簡(jiǎn)單的JavaScript互斥鎖分享
去年有幾個(gè)項(xiàng)目需要使用JavaScript互斥鎖,所以寫(xiě)了幾個(gè)類(lèi)似的,這是其中一個(gè):
//Published by Indream Luo
//Contact: indreamluo@qq.com
//Version: Chinese 1.0.0
!function ($) {
window.indream = window.indream || {};
$.indream = indream;
indream.async = {
//
//鎖
//lock: 鎖的編號(hào)
//action: 解鎖后執(zhí)行的方法
//
lock: function (lock, action) {
$.indream.async.waitings[lock] = $.indream.async.waitings[lock] || [];
$.indream.async.waitings[lock].push(action);
//如果該鎖未被使用,則當(dāng)前action阻塞該鎖
if (!$.indream.async.lockStatus[lock] && action) {
$.indream.async.lockStatus[lock] = true;
if (arguments.length > 2) {
var args = 'arguments[2]';
for (var i = 3; i < arguments.length; i++) {
args += ', arguments[' + i + ']';
}
eval('$.indream.async.action.call(action, ' + args + ')');
} else {
$.indream.async.action.call(action);
}
}
},
//
//解鎖
//lock: 鎖的編號(hào)
//
releaseLock: function (lock) {
$.indream.async.waitings[lock].shift();
//如果等待隊(duì)列有對(duì)象,則執(zhí)行等待隊(duì)列,否則解鎖
if ($.indream.async.waitings[lock].length) {
$.indream.async.waitings[lock][0]();
} else {
$.indream.async.lockStatus[lock] = false;
}
},
//
//鎖的狀態(tài)
//
lockStatus: [],
//
//等待事件完成
//lock:鎖編碼,相同的編碼將被整合成一個(gè)序列,觸發(fā)時(shí)同時(shí)觸發(fā)
//
wait: function (lock, action) {
$.indream.async.waitings[code] = $.indream.async.waitings[code] || [];
$.indream.async.waitings[code].push(action);
},
//
//等待序列
//
waitings: [],
//
//數(shù)據(jù)緩存
//
action: {
//
//監(jiān)聽(tīng)和回調(diào)的相關(guān)方法
//
callback: {
//
//監(jiān)聽(tīng)
//
listen: function (actionName, callback) {
var list = $.indream.async.action.callback.list;
list[actionName] = list[actionName] || [];
list[actionName].push(callback);
},
//
//回調(diào)
//
call: function (actionName, args) {
var list = $.indream.async.action.callback.list;
if (list[actionName] && list[actionName].length) {
for (var i in list[actionName]) {
$.indream.async.action.call(list[actionName][i], args);
}
}
},
//
//現(xiàn)有的回調(diào)列表
//
list: []
},
//
//根據(jù)方法是否存在和參數(shù)是否存在選擇適當(dāng)?shù)膱?zhí)行方式
//
call: function (action) {
if (action) {
if (arguments.length > 1) {
var args = 'arguments[1]';
for (var i = 2; i < arguments.length; i++) {
args += ', arguments[' + i + ']';
}
eval('action(' + args + ')');
} else {
action();
}
}
}
}
}
}(window.jQuery);
一個(gè)互斥鎖的幾個(gè)元素是:
•鎖與解鎖
•等待隊(duì)列
•執(zhí)行方法
以上鎖的用法:
//定義鎖的名稱(chēng)
var lock = 'scrollTop()';
//使用鎖
$.indream.async.lock(lock, function () {
var scrollTop = $(window).scrollTop();
var timer;
var fullTime = 100;
for (timer = 0; timer <= fullTime; timer += 10) {
setTimeout('$(window).scrollTop(' + (scrollTop * (fullTime - timer) / fullTime) + ');', timer);
}
//釋放鎖
setTimeout('$.indream.async.releaseLock("' + lock + '");', fullTime);
});
關(guān)于這次所的實(shí)現(xiàn),簡(jiǎn)單說(shuō)明下。
-自旋鎖還是信號(hào)量
JavaScript本身沒(méi)有鎖的功能,所以做的鎖都是在高層實(shí)現(xiàn)的。
依據(jù)JavaScript單線程的原理,JS的線程資源十分有限,非常不適合使用自旋鎖,所以選擇了使用信號(hào)量。
自旋鎖實(shí)現(xiàn)起來(lái)的樣子大致是這樣的,當(dāng)然do while更多用了:
while(true) {
//do something...
}
這樣必然需要占滿線程資源,可惜JS只有一條線程可以用來(lái)執(zhí)行,所以這樣做十分不適用。當(dāng)然,有需要可以選擇setInterval和clearInterval的組合去實(shí)現(xiàn),效果也會(huì)不錯(cuò)。
這里選用了信號(hào)量的方式,原理也簡(jiǎn)單,就如代碼那么短。工作的執(zhí)行順序大致是:
•把代碼段(回調(diào)的action)推入等待隊(duì)列
•判斷當(dāng)前鎖是否被持有,如果被持有則等待釋放,否則獲取該鎖,執(zhí)行回調(diào)
•當(dāng)鎖被釋放,則在等待隊(duì)列中shift出下一個(gè)回調(diào),將鎖傳遞給它并執(zhí)行
-自動(dòng)釋放還是手動(dòng)釋放
看起來(lái)最舒服的方式當(dāng)然是鎖住之后當(dāng)當(dāng)前程序執(zhí)行完就自動(dòng)釋放,不過(guò)這樣并不容易,因?yàn)橛懈嗟那闆r需要自定義釋放場(chǎng)景。
本身使用鎖的就是在異步中的方法,所以各種通常也會(huì)出現(xiàn)其他異步內(nèi)容,比如AJAX、jQuery動(dòng)畫(huà)。這個(gè)時(shí)候,自動(dòng)釋放就不符合需求了,因?yàn)閷?shí)際上真正的“執(zhí)行完畢”是在它內(nèi)部的異步回調(diào)完成后,也就是基本上只有開(kāi)發(fā)人員自己能把握,所以這里選擇了手釋放。
不過(guò)還是有缺陷的,就是重復(fù)釋放。
可以看到所有的鎖的對(duì)象都是公有的,或者應(yīng)該說(shuō)JS所有對(duì)象都是公有的,除非使局部變量在訪問(wèn)級(jí)別上進(jìn)行隔離。不過(guò)這里“鎖”本身就是個(gè)公共資源,所以沒(méi)辦法處理。
這里可以做的優(yōu)化應(yīng)該是像setInterval和clearInterval的那樣,以公共的鎖名稱(chēng)進(jìn)行加鎖,以私有的鎖ID進(jìn)行解鎖,就可以防止重復(fù)釋放了。不過(guò)上面這段老代碼中沒(méi)有,估計(jì)很快就會(huì)用到的了。
相關(guān)文章
JavaScript實(shí)現(xiàn)頁(yè)面實(shí)時(shí)顯示當(dāng)前時(shí)間的簡(jiǎn)單實(shí)例
這篇文章介紹了頁(yè)面實(shí)時(shí)顯示當(dāng)前時(shí)間的簡(jiǎn)單實(shí)例,有需要的朋友可以參考需要2013-07-07npm install報(bào)錯(cuò)無(wú)法創(chuàng)建packge.json文件的解決辦法
當(dāng)你在運(yùn)行 npm install 時(shí)遇到錯(cuò)誤,提示無(wú)法找到 package.json 文件,也沒(méi)有創(chuàng)建一個(gè) package.json 文件,只創(chuàng)建了一個(gè)package-lock.json文件,本文給大家介紹詳細(xì)的解決辦法,需要的朋友可以參考下2024-02-02JavaScript實(shí)現(xiàn)兩個(gè)Table固定表頭根據(jù)頁(yè)面大小自行調(diào)整
正如標(biāo)題所言?xún)蓚€(gè)Table固定表頭,可根據(jù)頁(yè)面大小自行調(diào)整使用JavaScript實(shí)現(xiàn),具體的示例如下,感興趣的朋友可以參考下2014-01-01easyui combobox開(kāi)啟搜索自動(dòng)完成功能的實(shí)例代碼
下面小編就為大家?guī)?lái)一篇easyui combobox開(kāi)啟搜索自動(dòng)完成功能的實(shí)例代碼。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-11-11echarts實(shí)現(xiàn)排名柱狀圖的示例代碼
在ECharts中,可以通過(guò)設(shè)置數(shù)據(jù)的順序來(lái)控制柱狀圖的排序,本文就介紹了echarts實(shí)現(xiàn)排名柱狀圖的示例代碼,具有一定的參考價(jià)值,感興趣的可以了解一下2023-09-09JavaScript QueryString解析類(lèi)代碼
以前寫(xiě)的一個(gè)解析QueryString的JS類(lèi),可以獲取或設(shè)置QueryString中的值,記下了方便找 :)2010-01-01JS實(shí)現(xiàn)隨機(jī)生成10個(gè)手機(jī)號(hào)的方法示例
這篇文章主要介紹了JS實(shí)現(xiàn)隨機(jī)生成10個(gè)手機(jī)號(hào)的方法,涉及javascript數(shù)值運(yùn)算與隨機(jī)數(shù)操作相關(guān)使用技巧,需要的朋友可以參考下2018-12-12JavaScript在for循環(huán)中綁定事件解決事件參數(shù)不同的情況
響應(yīng)一堆相似的事件,但是每個(gè)事件的參數(shù)都不同,在這種情況下就可以使用JavaScript 在for循環(huán)中綁定事件,下面有個(gè)不錯(cuò)的示例,大家可以參考下2014-01-01