javascript
JS异步阻塞的迷思
還是百度前端技術(shù)學(xué)院的“任務(wù)十九”可視化排序算法的題,在寫出快速排序算法之后,要求用動畫的形式把這個排序過程呈現(xiàn)出來。排序過程在CPU里不過是瞬間的事,但要轉(zhuǎn)換成“緩慢的”動畫效果給人類看,就不得不把這個過程速度降下來。
首先想到的是,Javascript有沒有像C++、Java那樣提供Sleep函數(shù)?
答案是:沒有。因為Javascript是單線程語言,一旦Sleep,整個程序就阻塞住了,瀏覽器也將失去響應(yīng)交互的能力,就像死了一樣。因此,通過寫個空循環(huán)來占用CPU時間以間接實現(xiàn)Sleep的方法,同樣不可取。
此路不通,嘗試別的思路。記得JS里有個常用的定時函數(shù)setTimeout,可以把指定的函數(shù)延時執(zhí)行。于是我修改了排序函數(shù)代碼,其中最后遞歸部分如下:
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); }這樣屏幕上看到的豎條確實可以從低到高排好序,但還是有問題:動畫的“幀數(shù)”也太少了吧?好多地方還沒有被改成“正在排序”的顏色,就直接變成有序的了。
原因也不難理解,參考這篇文章:《關(guān)于setTimeout,理解JavaScript定時機制》?,兩個遞歸函數(shù)是被緊挨著放進JS引擎的任務(wù)隊列的,前一個函數(shù)剛返回,就緊接著執(zhí)行后一個函數(shù),人的肉眼根本來不及看到GUI渲染的變化。
若是把兩個遞歸函數(shù)的延時設(shè)成不同值,比如一個500一個800,倒是可以解決這個問題,但我們看到的排序執(zhí)行順序?qū)靵y:一會兒在執(zhí)行左半邊的遞歸,一會兒又跳到右半邊執(zhí)行一下。而且“每一幀”之間的間隔時間也不一定。這也不是我們想要的結(jié)果。
還有沒有別的辦法,可以精確地控制執(zhí)行順序和時間間隔?答案是:有!受這篇文章(《jQuery鏈?zhǔn)讲僮鳌?#xff09;的啟發(fā),思路豁然開朗:只需用一個隊列將待執(zhí)行的函數(shù)一個個入隊,定時出隊并執(zhí)行出隊的函數(shù)就可以了。代碼如下:
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函數(shù)里調(diào)用出隊的函數(shù)、以及設(shè)置下次調(diào)用的時間間隔:
function doAction(){if(actionList.length==0){isReady=false;//還原所有染色區(qū)域**}if(!isReady) return;(actionList.shift())(); // 取出第一個函數(shù)并執(zhí)行 // 延時執(zhí)行下一個函數(shù) setTimeout(arguments.callee,interval); }開始時,將起始的visualSort函數(shù)入隊,再調(diào)用一下doAction函數(shù)。嗯!這種方法通過回調(diào)函數(shù)的形式有序調(diào)用遞歸函數(shù),效果是基本令人滿意的。
按上述代碼執(zhí)行的快排遞歸過程,本質(zhì)上是一個廣度優(yōu)先遍歷二叉樹的過程。要改成深度優(yōu)先也很簡單,加一個棧即可:
// 排序函數(shù)的最后部分 function visualSort(array,low,high,barArray) {// 排序**// 對左子序列排序的遞歸函數(shù)入隊,對右子序列排序的入棧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);}); }// 實現(xiàn)異步阻塞的函數(shù) function doAction(){if(leftList.length + rightList.length==0){isReady=false;//還原所有染色區(qū)域**}if(!isReady) return;if(leftList.length>0){(leftList.shift())(); // 出隊并執(zhí)行 }else if(rightList.length>0){(rightList.pop())(); // 彈棧并執(zhí)行 }// 延時執(zhí)行下一個函數(shù) setTimeout(arguments.callee,interval); }?
這樣就能看到豎條一根根從左到右排好啦。
?
轉(zhuǎn)載于:https://www.cnblogs.com/leegent/p/5326832.html
總結(jié)
- 上一篇: Android app被系统kill的场
- 下一篇: gradle idea java ssm