转: ES6异步编程: co函数库的含义与用法

co 函数库是著名程序员 TJ Holowaychuk 于2013年6月发布的一个小工具,用于 Generator 函数的自动执行。

比如,有一个 Generator 函数,用于依次读取两个文件。

var gen = function* (){
var f1 = yield readFile('./foo.txt');
var f2 = yield readFile('./bar.txt');
console.log(f1.toString());
console.log(f2.toString());
};

co 函数库可以让你不用编写 Generator 函数的执行器。

var co = require('co');
co(gen);

上面代码中,Generator 函数只要传入 co 函数,就会自动执行

co 函数返回一个 Promise 对象,因此可以用 then 方法添加回调函数。

co(gen).then(function(){
console.log('generator函数执行完毕了');
});

co函数库的原理

为什么 co 可以自动执行 Generator 函数?

前面文章说过,Generator 函数就是一个异步操作的容器。它的自动执行需要一种机制,当异步操作有了结果,能够自动交回执行权。

两种方法可以做到这一点。

  • 回调函数。将异步操作包装成 Thunk 函数,在回调函数里面交回执行权。
  • Promise 对象。将异步操作包装成 Promise 对象,用 then 方法交回执行权。

co 函数库其实就是将两种自动执行器(Thunk 函数和 Promise 对象),包装成一个库。使用 co 的前提条件是,Generator 函数的 yield 命令后面,只能是 Thunk 函数或 Promise 对象。

上一篇文章已经介绍了基于 Thunk 函数的自动执行器。下面来看,基于 Promise 对象的自动执行器。这是理解 co 函数库必须的。

基于Promise对象的自动执行

还是沿用上面的例子。首先,把 fs 模块的 readFile 方法包装成一个 Promise 对象。

/**
* 基于Promise的自动执行器
*/ var fs = require('fs'); var readFile = function(fileName) {
return new Promise(function(resolve, reject) {
fs.readFile(fileName, function(err, data) {
if(err) reject(err);
resolve(data);
});
});
}; var gen = function* (){
var f1 = yield readFile('./module-i.es');
var f2 = yield readFile('./module-i.js');
console.log(f1.toString());
console.log(f2.toString());
};

然后,手动执行上面的 Generator 函数。

var g = gen();
g.next().value.then(function(data) {
g.next(data).value.then(function(data){
g.next(data);
});
});

手动执行其实就是用 then 方法,层层添加回调函数。理解了这一点,就可以写出一个自动执行器。

function run (gen) {
var g = gen(); function next (data) {
var result = g.next(data);
if(result.done) return result.value;
result.value.then(next);
}
next();
} run(gen);

上面代码中,只要 Generator 函数还没执行到最后一步,next 函数就调用自身,以此实现自动执行。

co函数库的源码

co 就是上面那个自动执行器的扩展,它的源码只有几十行,非常简单。

首先,co 函数接受 Generator 函数作为参数,返回一个 Promise 对象。

function co(gen) {
var ctx = this; return new Promise(function(resolve, reject){ //在返回的 Promise 对象里面,co 先检查参数 gen 是否为 Generator 函数。如果是,就执行该函数,得到一个内部指针对象;如果不是就返回,并将 Promise 对象的状态改为 resolved 。 if(typeof gen === 'function') { gen = gen.call(ctx); }
if(!gen || typeof gen.next !== 'function') return resolve(gen);
}); //接着,co 将 Generator 函数的内部指针对象的 next 方法,包装成 onFulefilled 函数。这主要是为了能够捕捉抛出的错误。
onFulfilled();
function onFulfilled (res) {
var ret;
try {
ret = gen.next(res);
}catch(e){
return reject(e);
}
next(ret); //反复调用自身
} function next(ret) {
if (ret.done) return resolve(ret.value);
var value = toPromise.call(ctx, ret.value);
if (value && isPromise(value)) return value.then(onFulfilled, onRejected);
return onRejected(new TypeError('You may only yield a function, promise, generator, array, or object, '
+ 'but the following object was passed: "' + String(ret.value) + '"'));
}
});
}

上面代码中,next 函数的内部代码,一共只有四行命令。

  • 第一行,检查当前是否为 Generator 函数的最后一步,如果是就返回。
  • 第二行,确保每一步的返回值,是 Promise 对象。
  • 第三行,使用 then 方法,为返回值加上回调函数,然后通过 onFulfilled 函数再次调用 next 函数。
  • 第四行,在参数不符合要求的情况下(参数非 Thunk 函数和 Promise 对象),将 Promise 对象的状态改为 rejected,从而终止执行。

并发的异步操作

co 支持并发的异步操作,即允许某些操作同时进行,等到它们全部完成,才进行下一步。

这时,要把并发的操作都放在数组或对象里面。

// 数组
co(function* (){
var res = yield [
Promise.resolve(1);
Promise.resolve(2);
];
console.log(res);
}).catch(onerror); // 对象
co(function* (){
var res = yield {
'1': Promise.reolve(1);
'2': Promise.resovle(2);
};
console.log(res);
}).catch(onerror);

转: ES6异步编程: co函数库的含义与用法的更多相关文章

  1. 转: ES6异步编程:Thunk函数的含义与用法

    转: ES6异步编程:Thunk函数的含义与用法 参数的求值策略 Thunk函数早在上个世纪60年代就诞生了. 那时,编程语言刚刚起步,计算机学家还在研究,编译器怎么写比较好.一个争论的焦点是&quo ...

  2. 转: ES6异步编程:Generator 函数的含义与用法

    转: ES6异步编程:Generator 函数的含义与用法 异步编程对 JavaScript 语言太重要.JavaScript 只有一根线程,如果没有异步编程,根本没法用,非卡死不可. 以前,异步编程 ...

  3. ES6 异步编程之一:Generator

    Generator 生成器是es6原生提供的异步编程方案,其语法行为和传统函数完全不同,阮大的<ECMAScript 6 入门>一书中对生成器有比较详尽的介绍,还有一些其他的文章可以参考, ...

  4. python常用函数库及模块巧妙用法汇总

    在用python编写脚本或写程序过程中总要遇到一些对大文件或数据进行排序,计算,循环跌代等.我想下面这些函数库一定能用得到,总结如下:便于以后备查 列表去重(传说是列表去重最高效的方法): al = ...

  5. 异步编程(回调函数,promise)

    一.回调函数 ①概念:一般情况下,程序会时常通过API调用库里所预先备好的函数.但是有些库函数却要求应用先传给它一个函数,好在合适的时候调用,以完成目标任务.这个被传入的.后又被调用的函数就称为回调函 ...

  6. es6异步编程 Promise 讲解 --------各个优点缺点总结

    //引入模块 let fs=require('fs'); //异步读文件方法,但是同步执行 function read(url) { //new Promise 需要传入一个executor 执行器 ...

  7. es6异步编程

    https://blog.csdn.net/tcy83/article/details/80274772 等一系列文章

  8. async 函数的含义和用法

    Generator函数的含义与用法 Thunk函数的含义与用法 co函数库的含义与用法 async函数的含义与用法 一.终极解决 异步操作是 JavaScript 编程的麻烦事,麻烦到一直有人提出各种 ...

  9. JavaScript中异步编程

    一 关于事件的异步 事件是JavaScript中最重要的一个特征,nodejs就是利用js这一异步而设计出来的.所以这里讲一下事件机制. 在一个js文件中,如果要运行某一个函数,有2中手段,一个就是直 ...

随机推荐

  1. 南阳师范学院ACM集训队博客使用方法

    南阳师范学院ACM集训队博客使用方法 为方便大家交流,我们使用的是同一个用户名和密码,所以请不要随意修改用户名和密码,不然大家都登不上了,谢谢! 首先进入主页:http://www.cnblogs.c ...

  2. 【原】YUI3:js加载过程及时序问题

    时序问题在javascript中比较常见,尤其是在网络环境不稳定时以及某些浏览器本来版本中比较多,遇到此类问题,往往会使开发者非常头痛,问题的重现需要特定的环境,是偶发的,不容易重现.对于有经验的开发 ...

  3. 【学习笔记01】:hover为DIV添加鼠标悬停时改变颜色的效果

    :hover所有主流浏览器都支持(IE6.0以下支持不好,以后再学习用Javascript来实现悬停效果) 这是一个绿色底白色Icon的搜索按钮

  4. 在Google map图上做标记,并把标记相连接

    <!DOCTYPE html> <html> <head> <title>GeoLocation</title> <meta name ...

  5. phpcms 调用全站最新发布数据

    phpcms模板标签没有调用全站最新发布的数据 所以参考phpcms本身自带的lists方法写了一个Countlists调用全站数据 /** * 全站最热 * @param $data */ publ ...

  6. python运维开发(六)----模块续

    内容目录 反射 模块 os模块 sys模块 md5加密模块 re正则匹配模块 configparse模块 xml模块 shutil模块 subprocess模块 反射 利用字符串的形式去对象(模块)中 ...

  7. DataTables warning 错误警告

    今天使用 Charisma 框架的 jquery datatable 插件时出现如下错误: 搜索才发现 DataTables 目前不支持有单元格合并的表格.而且只要单元格数目不同就会有错误,不能使用搜 ...

  8. SQL顺序列找出断号

    select id from info id-----------123567810111215 (11 行受影响) 方法一: select (select max(id)+1 from Info w ...

  9. JAVA的IO运用

    IO OF JAVA想写好一篇关于JAVA的IO的文章不容易,因为它涉及的东西很多难以写得有深度和有思路.我虽不才但也写.这篇文章有我个人不少的见解,虽然涉足计算机不深但我不想用一大堆这个可能那个可能 ...

  10. 二叉查找树的Insert和Delete操作

    struct TreeNode{ SearchTree Left; SearchTree Right; ElementType Ele; }; /*递归一定有出口*/ /*递归代码就是要重复使用*/ ...