zoukankan      html  css  js  c++  java
  • Async 异步转同步详细流程解释

     

    安装

    npm install async --save

    地址

    https://github.com/caolan/async

    Async的内容主要分为三部分

    1. 流程控制: 简化九种常见的流程的处理
    2. 集合处理:如何使用异步操作处理集中的数据
    3. 工具类:几个常用的工具类

    本文主要介绍流程控制部分,后续内容持续更新,由于node.js是异步编程模型,有许多在同步编程中很容易做到的事情,现在就会变的很麻烦,并且存在很多的callback。但是,Async的流程控制给我们coder带来了许多便利。


    1.series(task, [callback])(多个函数依次执行,之间没有数据交换)

    有多个异步函数需要依次调用,一个完成之后才能执行下一个。各函数之间没有数据交换,仅仅需要保证其顺序执行。这时可以使用series。

    js代码

    step1(function (err, v1) {
        step2(function(err, v2){
            step3(function(err,v3){
                //code with the value [v1|v2|v3] or err
            });
        });
    });

    从上面的代码中可以看到,这些嵌套还是比较深的,如果操作更加复杂,那么会让代码的可读性降低。此外,在代码中忽略了对每一层err的处理,否则还要加上if(err) return callback(err); 那就更麻烦了。

    对于这种情况,我们可以使用async来处理

    var async = require('async');
    async.series([
        function(callback){
            step1(function(err, v1){
                //code with v1
                callback(err, v1);
            });
        },
        function(callback){step2(……)},
        function(callback){step3(……)}
    ],function(err, values){
        //code with the value [v1|v2|v3] or the err
    });
    

    上述async的详细解释为

    1. 依次执行一个函数数组中的每个函数,每一个函数执行完成之后才能执行下一个函数。
    2. 如果任何一个函数向它的回调函数中传了一个error,则后面的函数都不会被执行,并且将会立刻将该error以及已经执行了的函数的结果,传给series中最后的那个callback。
    3. 将所有的函数执行完后(没有出错),则会把每个函数传给其回调函数的结果合并为一个数组,传给series最后的那个callback。
    4. 还可以以json的形式提供tasks。每一个属性都会被当作函数来执行,并且结果也会以json形式传给series中最后的那个callback。这种方式可读性更高

    注: 多个series调用之间是不分先后的,因为series本身也是异步调用。

    2.parallel(tasks,[callback])(多个函数并行执行)

    并行执行多个函数,每个函数都是立刻执行,不需要等待其他函数先执行。传给最终callback的数组中的数据按照tasks声明的顺序,而不是执行完成的顺序

    如果某个函数出错,则立刻将err和已经执行完的函数的结果值传给parallel最终的callback。其它为执行完的函数的值不会传到最终数据,但要占个位置。

    同时支持json形式的tasks,其最终callback的结果也为json形式。

    正常执行的代码如下:

    async.parallel([
        function(callback){t.fire('f400', callback, 400)},
        function(callback){t.fire('f200', callback, 200)},
        function(callback){t.fire('f300', callback, 300)}
    ],function(err, results){
        log(err); //->undefined
        log(results); //->['f400', 'f200', 'f300']
    });

    中途出错的代码如下:

    async.parallel([
        function(callback){t.fire('f400', callback, 400)},
        function(callback){t.fire('f200', callback, 200)},
        function(callback){t.err('e300', callback, 300)}
    ], function(err, results){
        log(err); //->e300
        log(results); //->[, 'f200', undefined]
    });
    

    3.waterfall(tasks, [callback])(多个函数依次执行,且前一个的输出为后一个的输入)

    与series相似,按顺序依次执行多个函数。不同之处,每一个函数产生的值,都将传给下一个函数,如果中途出错,后面的函数将不会执行,错误信息以及之前产生的结果,都传给waterfall最终的callback。

    这个函数的名字为waterfall(瀑布),可以想象瀑布从上到下,承上启下,有点类似于linux中的pipes。 注意该函数不支持json格式的tasks。

    async.waterfall([
        function(callback){log('start'), callback(null, 3)},
        function(n, callback){log(n), t.inc(n, cb);/*inc为类似于i++的函数*/},
        function(n, callback){log(n), t.fire(n*n, cb);}
    ], function(err, results){
        log(err);
        log(results);
    });
    
    /**
        output
    
        start
        3
        4
        err: null
        results: 16
     */

    4.auto(tasks, [callback])(多个函数有依赖关系, 有的并行执行,有的一次执行)

    auto可以弥补parallel和series中的不足

    例如我要完成下面的事情

    1. 从某处取得数据
    2. 在硬盘上建立一个新的目录
    3. 将数据写入到目录下某文件
    4. 发送邮件
    async.auto({
        getData: function(callback){
            setTimeout(function(){
                console.log('got data');
                callback(null, 'mydata');
            }, 300);
        },
        makeFolder: function(callback){
            setTimeout(function() {
                console.log('made folder');
                callback(null, 'myfolder');
            }, 200);
        },
        writeFile:['getData', 'makeFolder', function(callback){
            setTimeout(function(){
                console.log('write file');
                callback(null, 'myfile');
            }, 300);
        }],
        emailFiles: ['writeFile', function(callback, results){
            log('send email');
            callback(null, results.writeFile);
        }]
    },function(err, results){
        log(err); //->null
        log(results);
        //made folder
        //got data
        //write file
        //send email
    
        /*
        results{
            makeFolder: 'myfolder',
            getData: 'mydata',
            writeFile: 'myfile',
            emailFiles: 'myfile'
        }
        */
    });

    5.whilst(test, fn, callback)(该函数的功能比较简单,条件变量通常定义在外面,可供每个函数访问。在循环中,异步调用时产生的值实际上被丢弃了,因为最后的callback只能传入错误信息,另外,第二个函数fn需要接受一个函数的cb, 这个cb最终必需被执行,用于表示出错或正常结束)

    var count = 0;
    async.whilst(
        //test
        function(){return count < 3;},
        function(cb){
            log(count);
            count++;
            setTimeout(cb, 1000);
        },
        function(err){
            //3s have passed
            log(err);
        }
    );
    /*
        0
        1
        2
        null
    */

    6.until(test, fn, callback)(与whilst相似,但判断条件相反)

    var count_until = 0;
    async.until(
        //test
        function(){ return count_until > 3;},
        function(cb){
            log(count_until);
            count_until++;
            setTimeout(cb, 1000);
        },
        function(err){
            //4s have passed
            log(err);
        }
    );
    /*
        0
        1
        2
        3
        null
    */

    7.queue(可设定worker数量的队列)

    queue相当于一个加强版的parallel, 主要限制了worker数量,不再一次性全部执行。当worker数量不够用时,新加入的任务将会排队等候,直到有新的worker可用。

    该函数有多个点可供回调,如worker用完时、无等候任务时、全部执行完时等。

    //定义一个queue, 设worker数量为2
    var q = async.queue(function(task, callback){
        log('worker is processing task: ' + task.name);
        task.run(callback);
    }, 2);
    //监听:如果某次push操作后, 任务数将达到或超过worker数量时, 将调用该函数
    q.saturated = function(){
        log('all workers to be used');
    }
    //监听:当最后一个任务交给worker时,将调用该函数
    q.empty = function(){
        log('no more tasks waiting');
    }
    //监听:当所有任务都执行完以后,将调用该函数
    q.drain = function(){
        log('all tasks have been processed');
    }
    
    //独立加入两个任务
    q.push({name : 't1', run: function(cb){
        log('t1 is running, waiting tasks:' + q.length());
        t.fire('t2', cb, 400); //400ms后执行
    }}, function(err){
        log('t1 executed');
    });
    
    log('pushed t1, waiting tasks:' + q.length());
    
    q.push({name: 't2', run: function(cb){
        log('t2 is running, waiting tasks:' + q.length());
        t.fire('t2', cb, 200); //200ms后执行
    }}, function(err){
        log('t2 executed');
    });
    
    log('pushed t2, waiting tasks:' + q.length());
    
    /**
        pushed t1, waiting tasks:1
        all workers to be used
        pushed t2, waiting tasks:2
        worker is processing task : t1
        t1 is running, waiting tasks: 1
        no more tasks waiting
        worker is processing task : t2
        t2 is running, waiting tasks: 0
        t2 executed
        t1 executed
        all tasks have been processed
     */

    8.iterator(tasks)(将几个函数包装为iterator)

    将一组函数包装成为一个iterator, 可通过next()得到以下一个函数为起点的新的iterator。该函数通常由async在内部使用,但如果需要时,也可在我们的代码中使用它。

    var iter = async.iterator([
        function(){console.log('111');},
        function(){console.log('222');},
        function(){console.log('333');}
    ]);
    
    var it1 = iter();
    it1();
    

    其中还包括了next()方法。

    9.nextTick(callback)(在nodejs与浏览器两边行为一致)

    nextTick的作用和nodejs的nextTick一样,都是把某个函数调用放在队列的尾部,但在浏览器端,只能使用setTimeout(callback, 0),但这个方法有时候会让其它高优先级的任务插到前面去。

    所以提供了这个nextTick,让同样的代码在服务器端和浏览器端表现一致。

    var calls = [];
    async.nextTick(function(){
        calls.push('two');
    });
    calls.push('one');
    async.nextTick(function(){
        console.log(calls); //-> ['one', 'two']
    })

    上述内容为学习笔记,大部分内容摘抄自alsotang的github中的async_demo,网址

  • 相关阅读:
    Phpstorm+Xdebug配置
    ThinkPHP5.0---URL访问
    Thinkphp5创建控制器
    ThinkPHP5.0的安装
    网页动态背景——随鼠标变换的动态线条
    git SSH
    [go] 循环与函数
    svg make a face
    Hello Vizhub
    Beetlsql自定义生成entity,mapper,md代码
  • 原文地址:https://www.cnblogs.com/juehai/p/9528743.html
Copyright © 2011-2022 走看看