嗯,我們繼續(xù)推進(jìn),我們要實(shí)現(xiàn) SmartQueue 的具體功能。上期分析過,SmartQueue 只有一個(gè)實(shí)例,因此我們決定直接在 SmartQueue 下面創(chuàng)建方法:
SmartQueue.init = function() { Q.forEach(function(queue) { queue.length = 0; }); };
這里用到 JavaScript 1.6 為 Array 對象提供的遍歷方法 forEach. 之所以這樣寫是因?yàn)槲覀兗俣ā巴獠看a”已經(jīng)在前面運(yùn)行過了。設(shè)置 Array 對象的 length 屬性為 0 導(dǎo)致,它被清空并且釋放所有的項(xiàng)(數(shù)組單元)。
最后一個(gè)方法 fire, 是整個(gè)組件最主要的方法,它負(fù)責(zé)對所有任務(wù)隊(duì)列進(jìn)行排序,并逐個(gè)執(zhí)行。由于代碼稍長了一點(diǎn),這里只介紹排序使用的算法和實(shí)現(xiàn)方式,完整代碼在這里。
var _dirty = true, // A flag indicates weather the Queue need to be fired. _sorted = [], index; // Sort all Queues. // ref: http://en.wikipedia.org/wiki/Topological_sorting var _visit = function(queue, task) { if(task._visited >= 1) { task._visited++; return; } task._visited = 1; // find out and visit all dependencies. var dependencies = [], i; task.dependencies.forEach(function(dependency) { i = _findTask(queue, dependency); if(i != -1) { dependencies.push(queue[i]); } }); dependencies.forEach(function(t) { _visit(queue, t); }); if(task._visited === 1) { _sorted[index].push(task); } }, _start = function(queue) { queue.forEach(function(task) { _visit(queue, task); }); }, _sort = function(suppress) { for(index = LEVEL_LOW; index <= LEVEL_HIGH; index++) { var queue = Q[index]; _sorted[index] = []; _start(queue); if(!suppress && queue.length > _sorted[index].length) { throw new Error('Cycle found in queue: ' + queue); } } };
我們將按任務(wù)指定的依賴關(guān)系對同一優(yōu)先級(jí)內(nèi)的任務(wù)進(jìn)行排序,確保被依賴的任務(wù)在設(shè)置依賴的任務(wù)之前運(yùn)行。這是一個(gè)典型的深度優(yōu)先的拓?fù)渑判騿栴},維基百科提供了一個(gè)深度優(yōu)先排序算法,大致描述如下:
圖片來自維基百科
- 訪問待排序的每一個(gè)節(jié)點(diǎn)
- 如果已經(jīng)訪問過了,則返回
- 否則標(biāo)記為已訪問
- 找出它連接(在這里是依賴)的每個(gè)節(jié)點(diǎn)
- 跳到內(nèi)層1遞歸訪問這些節(jié)點(diǎn)
- 訪問完了就把當(dāng)前節(jié)點(diǎn)加入已排序列表
- 繼續(xù)訪問下一個(gè)
如果 A 依賴 B, B 依賴 C, C 依賴 A, 那么這 3 個(gè)節(jié)點(diǎn)形成了循環(huán)依賴。 文中指出這個(gè)算法并不能檢測出循環(huán)依賴。通過標(biāo)記節(jié)點(diǎn)是否已訪問,可以解決循環(huán)依賴造成的遞歸死循環(huán)。我們來分析一下循環(huán)依賴的場景:
從節(jié)點(diǎn) A 出發(fā)的時(shí)候,它被標(biāo)記為已訪問,當(dāng)從節(jié)點(diǎn) C 再回到節(jié)點(diǎn) A 的時(shí)候,它已經(jīng)被訪問過了。不過這個(gè)時(shí)候 C 并不知道 A 是否在自己的上游鏈上,所以不能直接判定發(fā)生了循環(huán)依賴,因?yàn)?A 可能是其他已“處理”(跑完了內(nèi)層遞歸)過的節(jié)點(diǎn)。如果我們知道節(jié)點(diǎn)是不是第一次被訪問過,就可以判斷是哪一種情況。
改造一下上面的算法,將“是否已訪問”改成“訪問計(jì)數(shù)” (task._visited++)。僅當(dāng)節(jié)點(diǎn)被訪問過 1 次的時(shí)候 (task._visited === 1),才將其加入到已排序列表,全部遍歷完之后,如果待排序的節(jié)點(diǎn)數(shù)比已排序的多 (queue.length > _sorted[index].length),則表明待排序中多出的節(jié)點(diǎn)發(fā)生了循環(huán)依賴。
至此,隊(duì)列管理組件的編碼實(shí)現(xiàn)已經(jīng)完成。什么?怎么使用?很簡單啦:
var t1 = new SmartQueue.Task(function() { alert("Hello, world!"); }), t2 = new SmartQueue.Task(function() { alert("High level task has name"); }, 2, 'myname'); t1.register(); t2.register(); SmartQueue.fire();
更多功能,如任務(wù)的依賴,等待你去發(fā)掘哦。
本期貼出的代碼都是一些局部片段,部分 helper 方法代碼沒有貼出來。查看完整的代碼請?jiān)L問 這里 。后面我們將介紹如何管理組件文件,以及構(gòu)建組件,下期不見不散哦。
原文:http://ued.alipay.com/?p=1158
本文鏈接:http://m.95time.cn/tech/web/2009/7114.asp
出處:Alipay UED
責(zé)任編輯:bluehearts
上一頁 JavaScript組件之旅:編碼實(shí)現(xiàn)和算法 [2] 下一頁
◎進(jìn)入論壇網(wǎng)頁制作、WEB標(biāo)準(zhǔn)化版塊參加討論,我還想發(fā)表評論。
|