【javaScript随笔】06 javaScript的多线程探究

一、让setTimeout去吧

经常使用定时器的人都会使用setTimeout,而不使用setInterval,这个很好理解,setTimeout强调的是下次,而setInterval强调的是多次。

当去医院看病的时候,医生一般会告诉你下次什么时候来复查,而不是隔多久复查一次。

所以在可读性和可靠性上setTimeout更符合。

二、请先去排队

下面是一道常见的面试题

for(var i = 0; i < 3; i++) {
    setTimeout(function(){
        console.log(i);
    }, 0);
}

结果是输出: 3 3 3

它强调了setTimeout(fn, 0);的本质:把fn立刻添加到一个异步队列中,等主线程空闲,再排队执行。

等多久执行要看主线程什么时候释放,然后队有多长~,所谓的0只是马上去排队。

setTimeout的使用技巧一般是将复杂的运算放入队列,避免它阻塞了主线程。毫无疑问,你吃饭时间长,在只有一个餐位的情况下,人多,你就别想先吃。

三、vip通道和普通客户的区别

看下面一个常见性能问题

<button onclick="say()">点击</button>
<script type="text/javascript">
function say() {
    alert('hello');
};

for (var i = 0;  i < 10000; i++) {
    console.log(i);
}
</script>

明明是say方法先排队,但是反复点击确弹不出hello,而是要等for循环结束后,才可以正常点击。

这是因为for循环是主线程vip通道,say方法被放到其他队列里面了,主线程长期不释放,阻塞了say方法。

这种情况我们是不希望发生的,例如:在ajax回填一个大数据的时候,会占用主线程很长时间,而这个时候用户不能操作页面按钮。这得是多大的体验问题啊~

四、ajax也有vip

当一个ajax发起的过程中,是不会占用主线程的。这个时候是浏览器给ajax单独开了一个线程。

这个vip通道不能浪费~,因为java也是多线程,如果前后交互,能同时建立多个连接。这将是一个很好的性能优化方案,具体的要等我下次介绍。

五、单线程才是javaScript的本质

在javaScript中,所谈到的多线程(除了ajax),实际上都是一个误区:我们所号称的多线程,不过是强调了排队的次序。而打饭的窗口却只有一个。

所以说:单线程才是javaScript的本质。

六、从CPU调度到GUI的调度

java多线程是CPU的调度,javaScript在CPU上是不能改变的,但对于执行javaScript的浏览器而言,浏览器GUI渲染引擎是可以调度的。

调度的方法就是setTimeout。 可以设想,如果任何一个函数都可以拆解成一个个小的执行单元片段,那么用setTimeout去调度这个执行单元片段,就可以实现js的多线程。

七、场景

如果,有一个需求:需要一个小盒子,在x方向运动300px后停止3秒,运动到500px终止。在y方向运动400px后停止4秒,运动到600px终止。

这不是一个复杂的问题,不过我们可以尝试着用多线程去解一下。

我们可以构思下面的实现

<div id="cri" style="position: absolute;">盒子</div>
<script type="text/javascript">
var i = 0;
// x方向的线程
Thread.create(function b(){
    if (i === 300) {
        this.sleep(3000); // 休眠3秒
    }
    if (i === 500) {
        this.interrupt(); // 终止
    }
    cri.style.left = i + 'px';
    i++;
});

var j = 0;
// y方向的线程
Thread.create(function a(){
    if (j === 400) {
        this.sleep(4000);
    }
    if (j === 600) {
        this.interrupt();
    }
    cri.style.top = j + 'px';
    j++;
});
// 启动线程
Thread.start();
</script>

同时创建x和y两个方向上的线程,通过sleep和interrupt来简单控制线程。

八、实现方法

实现上面简单线程不难,我们可以创建一个队列,通过setTimeout去依次执行。interrupt实际上是把当前方法移除队列,sleep可以通过比较时间戳来实现。

(function(){
// 执行队列
var _executeQueue = [];
// 定时器句柄
var _timeID = 0;
// 防止Thread被实例
function Thread(){
    throw new Error("Thread cannot be instantiated");
}
// 创建线程的方法
Thread.create = function(fn){
    // 添加sleep方法
    fn.sleep = function(time){
        fn._executeTime = new Date().getTime() + time;
    };
    // 添加interrupt方法
    fn.interrupt = function() {
        fn._interrupt = true;
    };
    // 记录时间戳
    fn._executeTime = new Date().getTime();
    // 函数放入队列
    _executeQueue.push(fn);
    return fn;
};

// 线程执行方法
Thread.start = function() {
    if (_timeID) return; // 防止线程多次启动
    (function executeNext() { // 轮询执行队列
        if (_executeQueue.length > 0) {
            for(var i = 0; i < _executeQueue.length; i++) {
                var executeStatement = _executeQueue[i];
                // 比较时间戳时间sleep
                if (executeStatement._executeTime < new Date().getTime()) {
                    try{
                        // 执行队列里面的方法,改变上下文让线程内部可以使用this调用
                        executeStatement.call(executeStatement, null);
                    }catch(e){}
                }
                // 实现interrupt方法
                if (executeStatement._interrupt === true) {
                    _executeQueue.splice(i, 1);
                    i--;
                }
            };
            if (_timeID >= 0) {
                // 轮询执行
                _timeID = setTimeout(executeNext, 0);
            }
        }
    })();
};

// stop方法,停止所有线程
Thread.stop = function(){
    clearTimeout(_timeID);
    _timeID = 0;
};

window.Thread = Thread;
})();

九、h5的先不谈

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

三和小钢炮

谢谢客官~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值