• node js co分析1


    转载请注明: TheViper http://www.cnblogs.com/TheViper

    更好的可以看http://purplebamboo.github.io/2014/05/24/koa-source-analytics-1/

    co模块是什么?

    不同于promise,它是TJ大神基于ECMAScript 6 generator的异步控制解决方案.

    和promise一样,用co写出来的异步代码很优雅,一目了然,扩展性也很强。

    由于需要ECMAScript 6环境,所以需要下载版本新点的node.注意,不要去官网下,官网上的是最新的稳定版,要到http://nodejs.org/dist/下。另外现在官网的版本是0.10.35,需要下的是不稳定的0.11.x,至于具体版本号,co随时都在变,就用最新的好了。最后,运行node的时候要加上--harmony参数。

    ECMAScript 6 generator?

    比如,

    function* run() {
    console.log("step in child generator")
    var b = yield 'running';
    console.log(b);
    console.log("step out child generator")
    
    }
    //var runGenerator = run();
    function* start() {
      var a = yield 'start';
      console.log(a);
      yield *run();
      var c = yield 'end';
      console.log(c);
      return 'over';
    }
    var it = start();
    console.log(it.next());//Object {value: "start", done: false} 
    console.log(it.next(22));//22 step in child generator  object {value: 'running', done: false}
    console.log(it.next(333));//333 step out child generator  Object {value: 'end', done: false}
    console.log(it.next(444));//444 Object {value: "over", done: true}

    在function后面加上*表示这是一个generator function。

    var it=start();的时候,代码是没有开始执行的。it.next();相当于java,python的迭代器。

    yield相当于断点调试里面的单步进入。具体的,

    第一次调用it.next();,代码会执行到第一个yield声明的地方yield 'start';,此时还没有对a变量赋值。然后就是console.log返回it.next()的返回值,是一个对象,value是yield语句的值,这个值可以是字符串,函数,对象等,这里是字符串,done代表当前的generator function是否执行完毕。

    然后是it.next(22);,这个时候a赋值语句开始执行,实际上此时yield 'start'返回的就是22,也就是我们传的参数。一直执行到yield 'running';代码再次断点停住了。next方法的参数会成为上一个yield的返回值。

    输出22后,继续执行yield *run();里面的yield 'running';,然后和上面一样,并没有执行赋值语句。

    yield的参数如果是generator function,那么当执行yield generator function时,会不停的单步进入,直到最里面的那个yield的传入参数不是generator function为止。

    把上面的代码改一下,增加一个generator function。

    function* run1() {
    console.log("step in child generator1")
    var b = yield 'running1';
    console.log(b);
    console.log("step out child generator1")
    }
    
    function* run() {
    console.log("step in child generator")
    yield * run1();
    // console.log(b);
    console.log("step out child generator")
    }
    function* start() {
      var a = yield 'start';
      console.log(a);
      yield *run();
      var c = yield 'end';
      console.log(c);
      return 'over';
    }
    var it = start();
    console.log(it.next());//Object {value: "start", done: false} 
    console.log(it.next(22));//22 step in child generator step in child generator1 object {value: 'running1', done: false}
    console.log(it.next(333));//333 step out child generator1 step out child generator Object {value: 'end', done: false}
    console.log(it.next(444));//444 Object {value: "over", done: true}

    这下一目了然了。

    另外,如果generator function里面如果有return,下面的断点就不再起作用,而是单步跳出,提前返回,并且return的值作为代理调用的返回值。这个后面说co时可以看到有什么用处。

    co分析

    最新的co版本是4.1,co 4.x是基于promise重写的,稍微激进了点,这个后面会说到。这里先说co 3.x.

    首先看个例子,

    var co = require('co');
    var fs = require('fs');
    
    function size(file) {
      return function(fn){
        fs.stat(file, function(err, stat){
          if (err) return fn(err);
          fn(null, stat.size);
        });
      }
    }
    
    function *foo(){
      var a = yield size('node_modules/thunkify/.npmignore');
      var b = yield size('node_modules/thunkify/Makefile');
      var c = yield size('node_modules/thunkify/package.json');
      return [a, b, c];
    }
    
    co(function *(){
      var results = yield *foo();
      console.log(results);//[13,99,1201]
      return results;
    })();

    size()是个异步操作,依次读取文件的大小然后返回。

    这里的size()是改写过的,就像jquery里面的deffered,需要改写成一定的形式才能使用。这种形式叫thunk,是co里面yield可以接受的参数的一种。

    可以接受的参数形式

    有模块可以帮我们进行这样的改写,那就是thunkify.至于为什么要改写成这种形式,后面源码分析就可以看到原因。

    参数还可以接受array

    co(function *(){
      var a = size('.gitignore');
      var b = size('index.js');
      var c = size('Makefile');
      var res = yield [a, b, c];
      console.log(res);
      // => [ 13, 1687, 129 ]
    })()

    object

    co(function *(){
      var user = yield {
        name: {
          first: get('name.first'),
          last: get('name.last')
        }
      };
    })()

    generator

    function *foo(){
      var a = yield size('node_modules/thunkify/.npmignore');
      var b = yield size('node_modules/thunkify/Makefile');
      var c = yield size('node_modules/thunkify/package.json');
      return [a, b, c];
    }
    
    co(function *(){
      var results = yield *foo();
      console.log(results);//[13,99,1201]
      return results;
    })();

    promise

    co(function* () {
      var result = yield Promise.resolve(true);
      return result;
    }).then(function (value) {
      console.log(value);
    }, function (err) {
      console.error(err.stack);
    });

    Promise是自己构造的对象。

    另外,上面所有允许的参数形式都支持嵌套。

    function *foo(){
      var a = yield size('.gitignore');
      var b = yield size('Makefile');
      var c = yield size('package.json');
      return [a, b, c];
    }
    
    function *bar(){
      var a = yield size('examples/parallel.js');
      var b = yield size('examples/nested.js');
      var c = yield size('examples/simple.js');
      return [a, b, c];
    }
    
    co(function *(){
      var results = yield [foo(), bar()];
      console.log(results);
    })()

    如果有回调的话

    function *foo(){
      var a = yield size('node_modules/thunkify/.npmignore');
      var b = yield size('node_modules/thunkify/Makefile');
      var c = yield size('node_modules/thunkify/package.json');
      return [a, b, c];
    }
    
    co(function *(){
      var results = yield *foo();
      console.log(results);
      return results;
    })(function (err,args){
      console.log(args);
    });

    可以看到,会向回调传入两个参数,一个是错误信息,一个是返回值的数组。这是在size()里面就定义了的。

    function size(file) {
      return function(fn){
        fs.stat(file, function(err, stat){
          if (err) return fn(err);
          fn(null, stat.size);
        });
      }
    }

    里面的fn就是后面co要执行的回调。如果变成fn(stat.size),也就是只有一个参数。

    function size(file) {
      return function(fn){
        fs.stat(file, function(err, stat){
          if (err) return fn(null);
          fn(stat.size);
        });
      }
    }
    
    co(function *(){
      var results = yield *foo();
      console.log(results);
      return results;
    })(function (args){
      console.log(args);//13
    });

    这时只会返回第一个异步操作的结果。因为co规定了回调只允许传入前面说的那两个参数。如果异步操作的结果是多个值。

    function size(file) {
      return function(fn){
        fs.stat(file, function(err, stat){
          if (err) return fn(err);
          fn(null,stat.size,stat.atime);
        });
      }
    }
    
    function *foo(){
      var a = yield size('node_modules/thunkify/.npmignore');
      var b = yield size('node_modules/thunkify/Makefile');
      var c = yield size('node_modules/thunkify/package.json');
      return [a, b, c];
    }
    
    co(function *(){
      var results = yield *foo();
      console.log(results);
      return results;
    })(function (err,args){
      console.log(args);
    });

    可以看到结果是被添加到一个数组里面。

     

  • 相关阅读:
    Git教程
    Android 使用AIDL调用外部服务
    Android 不同文件名介绍
    详解Android首选项框架ListPreference
    Android 使用Telephony API
    Android ImageButton android:scaleType
    Java实现二维码QRCode的编码和解码
    java二维码生成与解析代码实现
    Java中转UTC时间字符串(含有T Z)为local时间
    http://www.yihaomen.com/article/java/302.htm
  • 原文地址:https://www.cnblogs.com/TheViper/p/4209009.html
Copyright © 2020-2023  润新知