• 树节点遍历-异步工作流方法研究


    功能背景

    大部网站都有菜单功能, 每个页面都有ajax更新页数据请求, 如何将页面上的所有菜单的页面都保存下来本地PC? 后续可以在本地PC上浏览页面。

    前面一个文章 利用phantomjs可以抓取单个页面的并保存到PC, 可以本地浏览。

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

    每个页面ajax数据更新, 需要等待若干时间, 所以在将页面保存PC的时刻, 需要在ajax数据返回之后,

    故需要在phantomjs代码中需要控制等待足够时间, 以确保页面的ajax更新执行完毕。 一般页面这个等待时间并不算长, 1-2完成。

    但是对于树状的菜单结构, 如何保证一个菜单都被访问过, 页面都被保存到PC,  然后再访问第二个菜单, 执行第二个菜单的访问和保存工作?

    如果每个菜单访问没有异步问题, 直接使用菜单的树的遍历机制即可。 但是树的遍历,每个节点访问都要考虑异步性访问, 正常的遍历就无能为力了。

    事实上, 这种异步业务执行的 流程控制, 是工作流研究的范畴。

    工作流

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

    工作流是将一组任务组织起来完成某个经营过程。在工作流中定义了任务的触发顺序和触发条件。每个任务可以由一个或多个软件系统完成,也可以由一个或一组人完成,还可以是由一个或多个人与软件系统协作完成。任务的触发顺序和触发条件用来定义并实现任务的触发、任务的同步和信息流(数据流)的传递。

    树前序遍历工作流

    是一种具有树状数据结构的任务节点,按照前序遍历的熟悉怒, 组成的工作流。

    每个树节点的执行,可以含有异步操作, 或者定时触发下一步操作。

    每个节点任务执行完毕后, 下一个被执行任务节点, 是本节点的前序遍历中的下一个任务节点。

    每个任务节点上的等待的动作, 就是异步特征。

    技术探查

    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>
  • 相关阅读:
    用户(三)
    首页和token验证(二)
    项目初始化和登录退出(一)
    VSCode设置vue/react模板
    Git操作
    C#可视化程序设计第三章(1,2)
    C#可视化程序设计第二章(3,4)
    SQL数据库第五章
    C#可视化程序设计第一章
    SQL数据库第四章
  • 原文地址:https://www.cnblogs.com/lightsong/p/6036477.html
Copyright © 2020-2023  润新知