还是百度前端技术学院的“任务十九”可视化排序算法的题,在写出快速排序算法之后,要求用动画的形式把这个排序过程呈现出来。排序过程在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);
}

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

JS异步阻塞的迷思的更多相关文章

  1. 前端迷思与React.js

    前端迷思与React.js 前端技术这几年蓬勃发展, 这是当时某几个项目需要做前端技术选型时, 相关资料整理, 部分评论引用自社区. 开始吧: 目前, Web 开发技术框架选型为两种的占 80% .这 ...

  2. JS魔法堂:深究JS异步编程模型

    前言  上周5在公司作了关于JS异步编程模型的技术分享,可能是内容太干的缘故吧,最后从大家的表情看出"这条粉肠到底在说啥?"的结果:(下面是PPT的讲义,具体的PPT和示例代码在h ...

  3. 深究JS异步编程模型

    前言  上周5在公司作了关于JS异步编程模型的技术分享,可能是内容太干的缘故吧,最后从大家的表情看出"这条粉肠到底在说啥?"的结果:(下面是PPT的讲义,具体的PPT和示例代码在h ...

  4. JS异步加载的三种方式

    js加载的缺点:加载工具方法没必要阻塞文档,过得js加载会影响页面效率,一旦网速不好,那么整个网站将等待js加载而不进行后续渲染等工作. 有些工具方法需要按需加载,用到再加载,不用不加载,. 默认正常 ...

  5. 关于JS异步加载方案

    javascript延迟加载的解决方案: 1.使用defer标签 <span style="font-size: small;"><script type=&qu ...

  6. js异步加载 defer和async 比较

    网上说法很多,很少一句话能总结清楚的,终于找到两句一针见血的描述,很到位: 相同点:都不阻塞DOM解析 defer  :顺序:保证先后顺序.解析:HTML 解析器遇到它们时,不阻塞(脚本将被异步下载) ...

  7. 转:web前端面试题合集 (Javascript相关)(js异步加载详解)

    1. HTTP协议的状态消息都有哪些? 1**:请求收到,继续处理2**:操作成功收到,分析.接受3**:完成此请求必须进一步处理4**:请求包含一个错误语法或不能完成5**:服务器执行一个完全有效请 ...

  8. 浅析JS异步执行机制

    前言 JS异步执行机制具有非常重要的地位,尤其体现在回调函数和事件等方面.本文将针对JS异步执行机制进行一个简单的分析. 从一份代码讲起 下面是两个经典的JS定时执行函数,这两个函数的区别相信对JS有 ...

  9. JS异步加载的三种方案

    js加载的缺点:加载工具方法没必要阻塞文档,个别js加载会影响页面效率,一旦网速不好,那么整个网站将等待js加载而不进行后续渲染等工作. 有些工具方法需要按需加载,用到再加载,不用不加载. 一.def ...

随机推荐

  1. Nido.Common.Utilities.MD5类

    using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Sec ...

  2. Android 自定义View可拖动移动位置及边缘拉伸放大缩小

    一.首先说一下定义这样一个View有什么用?在一些app中,需要设置头像,而用户选择的图片可能是使用摄像头拍摄,也可能是选择的相册里面的图片,总之,这样的图片大小不一,就比如在使用某个聊天软件的时候, ...

  3. Qualcomm Android display架构分析

    Android display架构分析(一) http://blog.csdn.net/BonderWu/archive/2010/08/12/5805961.aspx http://hi.baidu ...

  4. [经典] atoi && itoa

    atoi原型:int atoi(const char *nptr) atoi,需要考虑的内容: 1. 第一个字符为"-"时为负,系数为-1:为"+"时为正,系数 ...

  5. usaco 安慰奶牛

    Description 约翰有N个牧场,编号依次为1到N.每个牧场里住着一头奶牛.连接这些牧场的有P条 道路,每条道路都是双向的.第j条道路连接的是牧场Sj和Ej,通行需要Lj的时间.两牧场之 间最多 ...

  6. 佛山Uber优步司机奖励政策(2月1日~2月7日)

    滴快车单单2.5倍,注册地址:http://www.udache.com/ 如何注册Uber司机(全国版最新最详细注册流程)/月入2万/不用抢单:http://www.cnblogs.com/mfry ...

  7. DevExpress 用户控件 分页(下)

    分页控件调用 (1)初始化时: this.pageCtrl1.pageSize = 4; (2)数据绑定时: 从数据库中获取实时的 Public void LoadData(){ //这是只写有关分页 ...

  8. GWT事件与ELEMENT绑定

    GWT提供了DOM工具,利用美工做好的HTML页面,后台人员结合GWT来开发. Element button = DOM.getElementById("button_a"); D ...

  9. android自定义TabWidget

    在做项目的时候,需要用到这个选项卡,刚开始看了系统的tabwidget,囧了,底边有黑线不说,还不美观,扒了好多的网页发现前辈做的能够满足自己的需求,将代码修改了下,就能用喽,伟人说过,站在前辈的肩膀 ...

  10. vi 快捷键

    屏幕翻滚类命令Ctrl+u:向文件首翻半屏Ctrl+d:向文件尾翻半屏Ctrl+f:向文件尾翻一屏Ctrl+b:向文件首翻一屏 shift+g:到尾部 H:跳到第一行.M:跳到中间.L:跳到最后一行. ...