从co到koa01-co

thunk

  • 他的发展是由函数的求值策略的分歧决定的,两种求值策略
    • 传值调用,在进入函数体之前就直接执行完,把值传进去
    • 传名调用,将表达式传入函数体,只在用到他的时候求值
  • 传名函数的编译器实现,其实就是放入一个临时函数,再将临时函数传入函数体,这个临时函数就叫做thunk函数
  • js语言是传值调用,他的thunk含义有些不同,js中,thunk函数替换的不是表达式,而是多参数函数,将它替换成单参数的版本,且只接受回调函数作为参数
  • 任何有回调的函数都是可以搞成thunk形式的,下面是一个简单的生成器
var Thunk = function(fn){
    return function () {
      //先传入其他的参数初始化
      var args = Array.prototype.slice.call(arguments);
      //传入callback返回的函数
      return function(callback){
        args.push(callback);
        //实际调用的时候
        return fn.apply(this,args);
      }
    }
  }
  var readFileThunk = Thunk(fs.readFile);
  readFileThunk(fileA)(callback);

generator

  • Generator是为JavaScript设计的一种轻量级的协程。它通过yield关键字,可以控制一个函数暂停或者继续执行generator函数
function * hello (name) {
    yield 'your name is: ' + name
    return 'input name is: ' + name
}

const gen = hello('jinks')

//your name is: jinks
gen.next().value
//input name is: jinks
gen.next().value

CO

  • co的根本目的:将异步操作跟在yield后面,当异步操作完成并返回结果后,再触发下一次next() 。跟在yield后面的异步操作需要遵循一定的规范thunks和 promises
//简化版co
const fs = require('fs')
const Q = require('Q')
const readdir = Q.denodeify(fs.readdir)

//或者thunk规范
const thunkify = require('thunkify')
const readdir = thunkify(fs.readdir)

function co(generator) {
    return function (fn) {
        fn = fn || function () {}

        const gen = generator()
       
        function next (err, result) {
            if(err) {
                return fn(err)
            }
            
            const step = gen.next(result)
            
            if(!step.done) {
                // thunk
                step.value(next)
                // promise
                step.value.then(res => next(null, res)).catch(err => next(err))
            } else {
                fn(null, step.value)
            }
        }
        next()
    }
}

function *test() {
    const result = yield readdir(dir)
}

co(test)((err, result) => {
    console.log(err, result)
})
posted @ 2018-03-19 14:56  JinksPeng  阅读(144)  评论(0编辑  收藏  举报