• JS异步阻塞的迷思


    还是百度前端技术学院的“任务十九”可视化排序算法的题,在写出快速排序算法之后,要求用动画的形式把这个排序过程呈现出来。排序过程在CPU里不过是瞬间的事,但要转换成“缓慢的”动画效果给人类看,就不得不把这个过程速度降下来。

    首先想到的是,Javascript有没有像C++、Java那样提供Sleep函数?

    答案是:没有。因为Javascript是单线程语言,一旦Sleep,整个程序就阻塞住了,浏览器也将失去响应交互的能力,就像死了一样。因此,通过写个空循环来占用CPU时间以间接实现Sleep的方法,同样不可取。

    此路不通,尝试别的思路。记得JS里有个常用的定时函数setTimeout,可以把指定的函数延时执行。于是我修改了排序函数代码,其中最后递归部分如下:

    function visualSort(array,low,high,barArray) {
        //排序
        ****
        // 递归对左右子序列排序
        var fn=arguments.callee;
        var that=this;
        setTimeout(function(){
            fn.call(that,array,low,i-1,barArray);
        },500);
        setTimeout(function(){
            fn.call(that,array,i+1,high,barArray);
        },500);
    }

    这样屏幕上看到的竖条确实可以从低到高排好序,但还是有问题:动画的“帧数”也太少了吧?好多地方还没有被改成“正在排序”的颜色,就直接变成有序的了。

    原因也不难理解,参考这篇文章:《关于setTimeout,理解JavaScript定时机制》 ,两个递归函数是被紧挨着放进JS引擎的任务队列的,前一个函数刚返回,就紧接着执行后一个函数,人的肉眼根本来不及看到GUI渲染的变化。

    若是把两个递归函数的延时设成不同值,比如一个500一个800,倒是可以解决这个问题,但我们看到的排序执行顺序将会混乱:一会儿在执行左半边的递归,一会儿又跳到右半边执行一下。而且“每一帧”之间的间隔时间也不一定。这也不是我们想要的结果。

    还有没有别的办法,可以精确地控制执行顺序和时间间隔?答案是:有!受这篇文章(《jQuery链式操作》)的启发,思路豁然开朗:只需用一个队列将待执行的函数一个个入队,定时出队并执行出队的函数就可以了。代码如下:

    function visualSort(array,low,high,barArray){
        //排序
        ****
        //递归对左右子序列排序
        var fn=arguments.callee;
        var that=this;
        if(i > low) actionList.push(function(){
            fn.call(that,array,low,i-1,barArray);
        });
        if(i < high) actionList.push(function(){
            fn.call(that,array,i+1,high,barArray);
        });
    }

    然后在doAction函数里调用出队的函数、以及设置下次调用的时间间隔:

    function doAction(){
        if(actionList.length==0){
            isReady=false;
            //还原所有染色区域
            **
        }
        if(!isReady) return;
         (actionList.shift())(); // 取出第一个函数并执行  
        // 延时执行下一个函数
        setTimeout(arguments.callee,interval);
    }

    开始时,将起始的visualSort函数入队,再调用一下doAction函数。嗯!这种方法通过回调函数的形式有序调用递归函数,效果是基本令人满意的。

    按上述代码执行的快排递归过程,本质上是一个广度优先遍历二叉树的过程。要改成深度优先也很简单,加一个栈即可:

    // 排序函数的最后部分
    function visualSort(array,low,high,barArray) {
        // 排序
        **
        // 对左子序列排序的递归函数入队,对右子序列排序的入栈
        var fn=arguments.callee;
        var that=this;
        if(i > low) leftList.push(function(){
            fn.call(that,array,low,i-1,barArray);
        });
        if(i < high) rightList.push(function(){
            fn.call(that,array,i+1,high,barArray);
        });
    }
    
    // 实现异步阻塞的函数
    function doAction(){
        if(leftList.length + rightList.length==0){
            isReady=false;
            //还原所有染色区域
            **
        }
        if(!isReady) return;
        if(leftList.length>0){
            (leftList.shift())(); // 出队并执行    
        }
        else if(rightList.length>0){
            (rightList.pop())(); // 弹栈并执行
        }
        // 延时执行下一个函数
        setTimeout(arguments.callee,interval);
    }

    这样就能看到竖条一根根从左到右排好啦。

  • 相关阅读:
    背景大图隔几秒切换(非轮播,淡入淡出)--变形金刚joy007 项目总结
    call(京基填小票拍照片)
    加载图片、倒计时--Columbia项目总结
    kellogg项目总结
    js 跨域
    js 继承inheritance/extends
    一号店分享会
    日历,类似旅行网站的酒店入住
    js 数组的判断
    Building a Non-blocking TCP server using OTP principles
  • 原文地址:https://www.cnblogs.com/leegent/p/5326832.html
Copyright © 2020-2023  润新知