樹節點遍歷-異步工做流方法研究

功能背景

大部網站都有菜單功能, 每一個頁面都有ajax更新頁數據請求, 如何將頁面上的全部菜單的頁面都保存下來本地PC? 後續能夠在本地PC上瀏覽頁面。javascript

 

前面一個文章 利用phantomjs能夠抓取單個頁面的並保存到PC, 能夠本地瀏覽。html

http://www.cnblogs.com/lightsong/p/5971701.htmljava

每一個頁面ajax數據更新, 須要等待若干時間, 因此在將頁面保存PC的時刻, 須要在ajax數據返回以後,node

故須要在phantomjs代碼中須要控制等待足夠時間, 以確保頁面的ajax更新執行完畢。 通常頁面這個等待時間並不算長, 1-2完成。git

 

可是對於樹狀的菜單結構, 如何保證一個菜單都被訪問過, 頁面都被保存到PC,  而後再訪問第二個菜單, 執行第二個菜單的訪問和保存工做?github

若是每一個菜單訪問沒有異步問題, 直接使用菜單的樹的遍歷機制便可。 可是樹的遍歷,每一個節點訪問都要考慮異步性訪問, 正常的遍歷就無能爲力了。ajax

 

事實上, 這種異步業務執行的 流程控制, 是工做流研究的範疇。json

 

工做流

http://blog.csdn.net/wuluopiaoxue/article/details/6522908api

工做流是將一組任務組織起來完成某個經營過程。在工做流中定義了任務的觸發順序和觸發條件。每一個任務能夠由一個或多個軟件系統完成,也能夠由一個或一組人完成,還能夠是由一個或多我的與軟件系統協做完成。任務的觸發順序和觸發條件用來定義並實現任務的觸發、任務的同步和信息流(數據流)的傳遞。數組

 

樹前序遍歷工做流

是一種具備樹狀數據結構的任務節點,按照前序遍歷的熟悉怒, 組成的工做流。

每一個樹節點的執行,能夠含有異步操做, 或者定時觸發下一步操做。

每一個節點任務執行完畢後, 下一個被執行任務節點, 是本節點的前序遍歷中的下一個任務節點。

 

每一個任務節點上的等待的動做, 就是異步特徵。

 

技術探查

promise

首先技術上js提供promise可以很好處理, 每一個任務節點的異步執行 或者  定製觸發下一個任務節點的狀況。

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise

http://www.infoq.com/cn/news/2011/09/js-promise

可是其不能肯定工做流的順序, 仍是要依賴樹裝結構遍歷實現。

 

按照promise標準實現的庫 q.js

https://github.com/kriskowal/q/blob/v1/examples/all.js

https://github.com/kriskowal/q

 

casper

基於phantomjs的封裝, 支持promise風格訪問網頁, 比phantomjs原生提供的接口api,有些進步。

可是對於最簡單的線性工做流程(兩個任務), 也須要嵌套實現 then 中 thenOpen。

9 casper.start('http://google.com/', function() {
10     // 經過google表單搜索「CasperJS」關鍵字
11     this.fill('form[action="/search"]', { q: 'CasperJS' }, true);
12 });
13 casper.then(function() {
14     // 聚合「CasperJS」關鍵字搜索結果
15     links = this.evaluate(getLinks);
16     for (var i = 0; i < links.length; i++) {
17         casper.thenOpen(links[i]);
18         casper.then(function() {
19             var isFound = this.evaluate(function() {
20                 return document.querySelector('html').textContent.indexOf('CasperJS') >= 0;
21             });
22             console.log('CasperJS is found on ' + links[i] + ':' + isFound);
23         });
24     }
25 });
26 casper.run();

 

 

knysa

此工具避免了caperjs的對工做流的支持缺陷, 避免的嵌套, 支持了使用for while 等控制結構, 控制異步任務流的執行, 見

http://www.infoq.com/cn/articles/knysa-phantomjs-async-await?utm_campaign=rightbar_v2&utm_source=infoq&utm_medium=articles_link&utm_content=link_text

可使用同步的代碼風格書寫出, 異步任務流的流程控制。

對於任務流控制的中, 避免了 原生js的回調陷阱 和 caperjs的嵌套書寫 缺陷。

 

可是此框架是基於phantomjs的深度封裝, 只想部分引入此特性,對於現有已經熟悉或者已有項目積累的狀況, 總體替換此框架不合適。

 

demo:

kflow.knysa_open('http://google.com/');
10 kflow.knysa_fill('form[action="/search"]', { q: 'CasperJS' });
11 links = kflow.evaluate(getLinks);
12 i = -1;
13 while (++i < links.length) {
14     kflow.knysa_open(links[i]);
15     isFound = kflow.evaluate(function() {
16         return document.querySelector('html').textContent.indexOf('CasperJS') >= 0;
17     });
18     console.log('CasperJS is found on ' + links[i] + ':' + isFound);
19 }
20 phantom.exit();

 

 

ES6 async 和 await

js語言標準提供的新特性, 支持使用同步編碼的風格寫異步流程。

可是須要新的運行環境支持。

 

http://blog.csdn.net/exialym/article/details/52857171

demo:

function timeout(data, ms) {
  return new Promise((resolve) => {
    setTimeout(function(){
        resolve(data);
    }, ms);
  });
}
async function asyncPrint(value, ms) {
  //timeout會返回一個promise對象
  //await會等待這個對象中的resolve方法執行
  //並用其參數當作本身的返回值
  //值得注意的是await命令後面的Promise對象
  //運行結果多是rejected
  //因此最好把await命令放在try...catch代碼塊中
  //或者使用catch方法
  var a = await timeout(value,ms)
  .catch(function (err) {
    console.log(err);
  });
  console.log('a:'+a);
  return 'async over'
}
asyncPrint('hello world', 5000).then(v => console.log(v));
console.log('after async');
//after async
//a:hello world
//async over

 

Wind庫:

http://www.infoq.com/cn/articles/jscex-javascript-asynchronous-programming

 

提供 await async相似功能。

趙jeffery提供的庫,兼容低版本瀏覽器(環境不用考慮), 造福碼農,揚中國碼農威名。

https://github.com/JeffreyZhao/wind

 

demo

// 異步的比較操做 
var compareAsync = eval(Jscex.compile("async", function (x, y) {
    $await(Jscex.Async.sleep(10)); // 等待10毫秒
    return x - y;
}));

// 異步的交換操做
var swapAsync = eval(Jscex.compile("async", function (array, i, j) {
    var t = array[i];
    array[i] = array[j];
    array[j] = t;

    repaint(array); // 重繪

    $await(Jscex.Async.sleep(20)); // 等待20毫秒
}));

// 異步的冒泡排序 
var bubbleSortAsync = eval(Jscex.compile("async", function (array) {
    for (var i = 0; i < array.length; i++) {
        for (var j = 0; j < array.length - i; j++) {
            // 執行異步的比較操做
            var r = $await(compareAsync(array[j], array[j + 1]));
            if (r > 0) {
                // 執行異步的交換操做
                $await(swapAsync(array, j, j + 1));
            }
        }
    }
}));

// 調用
var array = ...; // 初始化數組
bubbleSortAsync(array).start();

 

Wind實現樹遍歷業務流

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
    <title>Hanoi - Wind.js Samples</title>
    
    <script src="../../../src/wind-core.js"></script>
    <script src="../../../src/wind-compiler.js"></script>
    <script src="../../../src/wind-builderbase.js"></script>
    <script src="../../../src/wind-async.js"></script>
    
</head>
<body>

    <script>

var treejson = {value:"root", children:[
    {value:"childone"}, {value:"childtwo"}
]};

// 樹前序遍歷方法
function treeTraverse_preOder (treejson) {
    if ( !treejson )
    {
        return;
    }

    console.log("node value ="+treejson.value);

    if ( !treejson.children ) 
    {
        return
    }

    for (var i = 0; i < treejson.children.length; i++) {
        var child = treejson.children[i];
        treeTraverse_preOder(child)
    }
}

treeTraverse_preOder(treejson)

// 下面使用wind執行時間空格前序遍歷

// 異步的輸出操做 
var printAsync = eval(Wind.compile("async", function (treejson) {
    console.log(treejson.value)

    $await(Wind.Async.sleep(2000)); // 等待2秒
    return true;
}));

// 異步的樹遍歷操做 
var treeTraverseAsync = eval(Wind.compile("async", function (treejson) {
    if ( !treejson )
    {
        return;
    }

    $await(printAsync(treejson));

    if ( !treejson.children ) 
    {
        return
    }

    for (var i = 0; i < treejson.children.length; i++) {
        var child = treejson.children[i];
        $await(treeTraverseAsync(child));
    }
}));

treeTraverseAsync(treejson).start();


    </script>
    
</body>
</html>
相關文章
相關標籤/搜索