关闭搜索(ESC)
搜索标签:

从co看如何优雅的写异步

2015-09-11 浏览:1626 标签: generator 异步

介绍

express是我见过最轻量级的nodejs框架,但是TJ重新写了一个koa框架,利用generator实现了重复繁琐的回调,并且不绑定任何中间件,完全轻量级,可以自由发挥。

异步

在写异步的时候,会出现两个问题:

1:必须在回调函数里处理返回值
2:复杂情况下导致嵌套过深

举个例子,文件读取:

嵌套过多,将会导致代码混乱,简直就是代码黑洞。

generator

generator是ES6(代号为harmony)里的一个新特性。它的作用可以将一个函数被执行后还能暂停。

generator的定义方式非常简单,比普通函数多一个*

var compute = function *(a,b) {
    var r1 = a+b;
    yield r1;
    var r2 = a-b;
    yield r2;
    var r3 = a*b;
    yield r3;
    var r4 = a/b;
    yield r4;
    return r4;
};
var generator = compute(2,4);
console.log(generator.next()); // {value: 6,done: false}

Generator可以理解为一个数据接口,将compute函数赋值为generator对象,generator.next()执行函数。

但是yield在这里是非常重要的,能够将generator内部的逻辑切割为多块运行。

yield不仅是切割功能,每次执行.next()函数,都会返回两个属性,一个是布尔类型done,另一个是返回值value。

利用这个功能改造异步

需要用到高阶函数,首先我们需要修改回调函数。如下:

var helper = function (fn) {
  return function () {
    var args = [].slice.call(arguments);
    var pass;
    args.push(function () { // 在回调函数中植入收集逻辑
      if (pass) {
        pass.apply(null, arguments);
      }
    });
    fn.apply(null, args);

    return function (fn) { // 传入一个收集函数
      pass = fn;
    };
  };
};

这个函数的作用就是收集参数,定义一个实际被调用的函数,当这个函数被执行后,真正执行原来的函数,这个函数的作用是可以随时执行新的逻辑。

现在的实现方式是:

var readFile = helper(fs.readFile);
var flow = function * () {
    var text = yield readFile('file1.txt', 'utf8');
    console.lg(text);
}
var generator = flow();
generator.next();

ok现在就是实现了一个同步写异步的方法。然后为了更好用,我们封装一下这个函数。实现一个co函数

var co = function(flow) {
    var generator = flow();
    var next = function(data) {
        var result = generator.next(data);
        if(!result.done) {
            result.value(function(err, data){
                if(err)
                    throw err;
                next(data);
            });
        }
    };
    next();
};

然后我的调用方法就是:

co(function * () {
    var str = yield readFile('file1.txt', 'utf8');
    console.log(str);
});

现在实现了一个同步写异步,并且能写出比较优雅的代码功能。

koa

TJ在KOA里实现了co这个功能,方式和这个差不多,co地址

实现的方式:

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

然后我们如何在koa里写优雅的代码呢?

var request = require('co-request');
function * list() {
    var result = yield request('');
    console.log(data);
}

总结

为什么需要讲到generator,co,koa?首先解决了异步的复杂性,然后通过接口的调用,实践了nodejs层用来前后端分离的可行性,为后续的前后端分离提供了基础。

添加评论