简单的JavaScript互斥锁分享


Posted in Javascript onFebruary 02, 2014

去年有几个项目需要使用JavaScript互斥锁,所以写了几个类似的,这是其中一个:

//Published by Indream Luo
//Contact: indreamluo@qq.com
//Version: Chinese 1.0.0
!function ($) {
    window.indream = window.indream || {};
    $.indream = indream;
    indream.async = {
        //
        //锁
        //lock: 锁的编号
        //action: 解锁后执行的方法
        //
        lock: function (lock, action) {
            $.indream.async.waitings[lock] = $.indream.async.waitings[lock] || [];
            $.indream.async.waitings[lock].push(action);
            //如果该锁未被使用,则当前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: 锁的编号
        //
        releaseLock: function (lock) {
            $.indream.async.waitings[lock].shift();
            //如果等待队列有对象,则执行等待队列,否则解锁
            if ($.indream.async.waitings[lock].length) {
                $.indream.async.waitings[lock][0]();
            } else {
                $.indream.async.lockStatus[lock] = false;
            }
        },
        //
        //锁的状态
        //
        lockStatus: [],
        //
        //等待事件完成
        //lock:锁编码,相同的编码将被整合成一个序列,触发时同时触发
        //
        wait: function (lock, action) {
            $.indream.async.waitings[code] = $.indream.async.waitings[code] || [];
            $.indream.async.waitings[code].push(action);
        },
        //
        //等待序列
        //
        waitings: [],
        //
        //数据缓存
        //
        action: {
            //
            //监听和回调的相关方法
            //
            callback: {
                //
                //监听
                //
                listen: function (actionName, callback) {
                    var list = $.indream.async.action.callback.list;
                    list[actionName] = list[actionName] || [];
                    list[actionName].push(callback);
                },
                //
                //回调
                //
                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);
                        }
                    }
                },
                //
                //现有的回调列表
                //
                list: []
            },
            //
            //根据方法是否存在和参数是否存在选择适当的执行方式
            //
            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);

一个互斥锁的几个元素是:

•锁与解锁
•等待队列
•执行方法
以上锁的用法:

//定义锁的名称
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);
});

关于这次所的实现,简单说明下。

-自旋锁还是信号量
JavaScript本身没有锁的功能,所以做的锁都是在高层实现的。

依据JavaScript单线程的原理,JS的线程资源十分有限,非常不适合使用自旋锁,所以选择了使用信号量。

自旋锁实现起来的样子大致是这样的,当然do while更多用了:

while(true) {
    //do something...
}

这样必然需要占满线程资源,可惜JS只有一条线程可以用来执行,所以这样做十分不适用。当然,有需要可以选择setInterval和clearInterval的组合去实现,效果也会不错。

这里选用了信号量的方式,原理也简单,就如代码那么短。工作的执行顺序大致是:

•把代码段(回调的action)推入等待队列
•判断当前锁是否被持有,如果被持有则等待释放,否则获取该锁,执行回调
•当锁被释放,则在等待队列中shift出下一个回调,将锁传递给它并执行
 

-自动释放还是手动释放
看起来最舒服的方式当然是锁住之后当当前程序执行完就自动释放,不过这样并不容易,因为有更多的情况需要自定义释放场景。

本身使用锁的就是在异步中的方法,所以各种通常也会出现其他异步内容,比如AJAX、jQuery动画。这个时候,自动释放就不符合需求了,因为实际上真正的“执行完毕”是在它内部的异步回调完成后,也就是基本上只有开发人员自己能把握,所以这里选择了手释放。

不过还是有缺陷的,就是重复释放。

可以看到所有的锁的对象都是公有的,或者应该说JS所有对象都是公有的,除非使局部变量在访问级别上进行隔离。不过这里“锁”本身就是个公共资源,所以没办法处理。

这里可以做的优化应该是像setInterval和clearInterval的那样,以公共的锁名称进行加锁,以私有的锁ID进行解锁,就可以防止重复释放了。不过上面这段老代码中没有,估计很快就会用到的了。

Javascript 相关文章推荐
jquery $.getJSON()跨域请求
Dec 21 Javascript
用jQuery实现一些导航条切换,显示隐藏的实例代码
Jun 08 Javascript
javascript移出节点removeChild()使用介绍
Apr 03 Javascript
Node.js中使用事件发射器模式实现事件绑定详解
Aug 15 Javascript
js获取页面传来参数的方法
Sep 06 Javascript
JS仿Windows开机启动Loading进度条的方法
Feb 26 Javascript
JavaScript实现快速排序的方法
Jul 31 Javascript
前端面试题及答案整理(二)
Aug 26 Javascript
vuejs使用递归组件实现树形目录的方法
Sep 30 Javascript
Vue组件中的data必须是一个function的原因浅析
Sep 03 Javascript
基于vue实现一个神奇的动态按钮效果
May 15 Javascript
详解vue中v-bind:style效果的自定义指令
Jan 21 Javascript
在百度知道团队中快速审批新成员的js脚本
Feb 02 #Javascript
基于jquery的simpleValidate简易验证插件
Jan 31 #Javascript
基于JQuery实现的图片自动进行缩放和裁剪处理
Jan 31 #Javascript
jquery为页面增加快捷键示例
Jan 31 #Javascript
通过pjax实现无刷新翻页(兼容新版jquery)
Jan 31 #Javascript
jquery 页面滚动到底部自动加载插件集合
Jan 31 #Javascript
基于JQuery实现滚动到页面底端时自动加载更多信息
Jan 31 #Javascript
You might like
PHP实现定时生成HTML网站首页实例代码
2008/11/20 PHP
php学习之 认清变量的作用范围
2010/01/26 PHP
php中使用redis队列操作实例代码
2013/02/07 PHP
20个2014年最优秀的PHP框架回顾
2014/10/22 PHP
php中解析带中文字符的url函数分享
2015/01/20 PHP
浅析PHP开发规范
2018/02/05 PHP
PHP htmlspecialchars()函数用法与实例讲解
2019/03/08 PHP
Laravel框架源码解析之模型Model原理与用法解析
2020/05/14 PHP
jquery 使用点滴函数代码
2011/05/20 Javascript
点击显示指定元素隐藏其他同辈元素的方法
2014/02/19 Javascript
Area 区域实现post提交数据的js写法
2014/04/22 Javascript
js获取所有checkbox的值的简单实例
2016/05/30 Javascript
Angularjs之filter过滤器(推荐)
2016/11/27 Javascript
JS多文件上传的实例代码
2017/01/11 Javascript
vue 和vue-touch 实现移动端左右导航效果(仿京东移动站导航)
2017/04/22 Javascript
mac下的nodejs环境安装的步骤
2017/05/24 NodeJs
iscroll动态加载数据完美解决方法
2017/07/18 Javascript
微信小程序icon组件使用详解
2018/01/31 Javascript
Vue自定义全局Toast和Loading的实例详解
2019/04/18 Javascript
js计时事件实现圆形时钟
2020/03/25 Javascript
[45:52]2018DOTA2亚洲邀请赛 4.1小组赛 A组加赛 LGD vs Liquid
2018/04/02 DOTA
[01:07:41]IG vs VGJ.T 2018国际邀请赛小组赛BO2 第一场 8.18
2018/08/19 DOTA
Python随机生成均匀分布在三角形内或者任意多边形内的点
2017/12/14 Python
python版本的仿windows计划任务工具
2018/04/30 Python
Django Form 实时从数据库中获取数据的操作方法
2019/07/25 Python
flask框架jinja2模板与模板继承实例分析
2019/08/01 Python
python小项目之五子棋游戏
2019/12/26 Python
使用Python Tkinter实现剪刀石头布小游戏功能
2020/10/23 Python
瑞士图书网站:Weltbild.ch
2019/09/17 全球购物
俄罗斯香水和化妆品在线商店:Aroma-butik
2020/02/28 全球购物
保密工作责任书
2014/04/16 职场文书
校长创先争优承诺书
2014/08/30 职场文书
群众路线教育实践活动的心得体会
2014/09/03 职场文书
2014民事授权委托书范本
2014/09/29 职场文书
公务员考察材料范文
2014/12/23 职场文书
2016年大学自主招生自荐信范文
2015/03/24 职场文书