es6 co的使用与实现

作者: mytac | 来源:发表于2017-12-06 16:50 被阅读45次

虽然现在异步解决方案都用async/await了,但co是一个小巧而精致的库,我们也可以学习一下他的实现原理。
本文首发于本人的github,欢迎star与watch~~OVO

什么是co

co是一个函数库,用于generator的自动执行。

我们先定义一个generator,用来异步读下面几个文件

const gen=function *(){

    const b=yield read('b.js')
    console.log(b.length)

    const c=yield read('c.js')
    console.log(c.length)
}

其中,我们定义的read异步读取文件的函数为:

function read(file){
    fs.readFile(file,'utf8',(err,res)=>{
        g.next(res)
    })
}

const g=gen()
g.next() //将分别输出两个文件的长度

co的使用

虽然上面的代码也可以顺利异步执行generator函数,但是需要把next()写到异步函数中,
增加了耦合度,而且还要创建generator的实例,代码很繁琐。

我们使用co重构上面的代码:

首先要改造一下异步函数的结构,co规定所有的异步函数必须是偏函数,称之为thunk函数:

function read(file){
    return (fn)=>{
        fs.readFile(file,'utf8',fn)
    }
}

之后,只要把generator传到co中就大功告成了

const co=require('co')

co(gen)() //分别输出两个文件的长度

实现一个简单的co

function co(fn) {
    return function(done) {
        const ctx = this;
        const gen = fn.call(ctx);
        let it = null;
        function _next(err, res) {
            if(err) res = err;
            it = gen.next(res);
            //{value:function(){},done:false}
            if(!it.done){
                it.value(_next);
            }
        }
        _next();
    }
}

这部分的核心代码为:

function _next(err, res) {
    if(err) res = err;
    it = gen.next(res);
    //{value:function(){},done:false}
    if(!it.done){
        it.value(_next);
    }
}

_next();

他的目的是递归调用generator的next(),直到done为true时,停止递归。

在调用第一次next()的之后,it为:

it={
    value:(fn)=>{fs.readFile(file,'utf8',fn)},
    done:false
}

it.donefalse的情况下,将_next()这个函数作为实参传到(fn)=>{fs.readFile(file,'utf8',fn)}中。
这时才正式执行读取文件的操作,res为读取的文件内容,之后再次执行_next()

第二次调用_next()后,将res值塞回gen中,之后执行:

 const b=res
 console.log(b.length)

之后往后执行,遇到yield又中断了,再次读取c.js中的内容,之后又进入_next(),再把res值塞回gen中,和上次一样执行下列代码:

 const c=res
 console.log(c.length)

之后再触发_next()知道done状态为true

全部代码

这里的执行顺序比较乱,强烈建议大家去加断点多跑几遍!

function co(fn) {
    return function(done) {
        const ctx = this;
        const gen = fn.call(ctx);
        let it = null;
        function _next(err, res) {
            if(err) res = err;
            it = gen.next(res);
            //{value:function(){},done:false}
            if(!it.done){
                it.value(_next);
            }
        }
        _next();
    }
}

function read(file){
    return (fn)=>{
        fs.readFile(file,'utf8',fn)
    }
}

const gen=function *(){
    const b=yield read('error.js')
    console.log(b.length)

    const c=yield read('package.json')
    console.log(c.length)
}

co(gen)()

相关链接

  1. co 函数库的含义和用法
  2. Generator 函数的含义与用法

相关文章

  • es6 co的使用与实现

    虽然现在异步解决方案都用async/await了,但co是一个小巧而精致的库,我们也可以学习一下他的实现原理。本文...

  • 微信小程序中使用co来处理异步流程

    co是一个基于ES6 Generator特性实现的【异步流程同步化】写法的工具库。 co需要使用Promise特性...

  • JS中同步心得

    不使用co模块 异步代码 2种方式 then 实现同步 async 实现同步 co模块实现同步(co+yield)...

  • JavaScript设计模式-0001---迭代器模式

    使用闭包实现迭代器 test es6使用 生成器实完成迭代器模式 使用es6的生成器实现数据自增 迭代器/es6的...

  • JavaScripts数据的深拷贝和浅拷贝

    几种浅拷贝的实现方式 es6扩展运算符「...」 Object.assign() 数组中的slice() & co...

  • ES6中 import、 export default 和 ex

    ES6实现了模块功能export和import 在ES6出现之前,已经出现了解决js模块加载的方案,最主要的是Co...

  • Generator

    基本用法 更详细用法看ES6 Generator 迭代器 co 即generator的执行器,实现依靠promis...

  • 原生js实现数据双向绑定效果

    通过Object.defineProperty实现效果 使用ES6 class实现数据双向绑定

  • es6常用语法

    const 和 let 在es6语法规范中,尽量使用let替代var,如果在const 和let中选,优先使用co...

  • 第4章 ES6模块化

    目标 模块化系统演进 ES6模块化实现 模块化概述 在 ES6 之前,社区制定了一些模块加载方案,最主要的有 Co...

网友评论

    本文标题:es6 co的使用与实现

    本文链接:https://www.haomeiwen.com/subject/svrlixtx.html