co 函数库的含义和用法

以下是《深入掌握 ECMAScript 6 异步编程》系列文章的第三篇。

一、什么是 co 函数库?

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


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

 var gen = function* (){ var f1 = yield readFile('/etc/fstab'); var f2 = yield readFile('/etc/shells');
 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 函数执行完成'); }) 

上面代码中,等到 Generator 函数执行结束,就会输出一行提示。

二、 co 函数库的原理

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

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

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

(1)回调函数。将异步操作包装成 Thunk 函数,在回调函数里面交回执行权。

(2)Promise 对象。将异步操作包装成 Promise 对象,用 then 方法交回执行权。

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

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

三、基于 Promise 对象的自动执行

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

 var fs = require('fs'); var readFile = function (fileName){ return new Promise(function (resolve, reject){
 fs.readFile(fileName, function(error, data){ if (error) reject(error); resolve(data); }); }); }; var gen = function* (){ var f1 = yield readFile('/etc/fstab'); var f2 = yield readFile('/etc/shells');
 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(function(data){ next(data); }); } 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 。

 function co(gen) { var ctx = this; return new Promise(function(resolve, reject) { if (typeof gen === 'function') gen = gen.call(ctx); if (!gen || typeof gen.next !== 'function') return resolve(gen); }); } 

接着,co 将 Generator 函数的内部指针对象的 next 方法,包装成 onFulefilled 函数。这主要是为了能够捕捉抛出的错误。

 function co(gen) { var ctx = this; return new Promise(function(resolve, reject) { if (typeof gen === 'function') gen = gen.call(ctx); if (!gen || typeof gen.next !== 'function') return resolve(gen); onFulfilled(); function onFulfilled(res) { var ret; try {
 ret = gen.next(res); } catch (e) { return reject(e); } next(ret); } }); } 

最后,就是关键的 next 函数,它会反复调用自身。

 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.resolve(1), 2: Promise.resolve(2), };
 console.log(res); }).catch(onerror); 

(完)

时间: 2024-08-03 15:43:02

co 函数库的含义和用法的相关文章

async 函数的含义和用法

本文是<深入掌握 ECMAScript 6 异步编程>系列文章的最后一篇. Generator函数的含义与用法 Thunk函数的含义与用法 co函数库的含义与用法 async函数的含义与用法 一.终极解决 异步操作是 JavaScript 编程的麻烦事,麻烦到一直有人提出各种各样的方案,试图解决这个问题. 从最早的回调函数,到 Promise 对象,再到 Generator 函数,每次都有所改进,但又让人觉得不彻底.它们都有额外的复杂性,都需要理解抽象的底层运行机制. 异步I/O不就是读取一个

Thunk 函数的含义和用法

本文是<深入掌握 ECMAScript 6 异步编程>系列文章的第二篇. Generator函数的含义与用法 Thunk函数的含义与用法 co函数库的含义与用法 async函数的含义与用法 一.参数的求值策略 Thunk函数早在上个世纪60年代就诞生了. 那时,编程语言刚刚起步,计算机学家还在研究,编译器怎么写比较好.一个争论的焦点是"求值策略",即函数的参数到底应该何时求值. var x = 1; function f(m){ return m * 2; } f(x +

Generator 函数的含义与用法

本文是<深入掌握 ECMAScript 6 异步编程>系列文章的第一篇. Generator函数的含义与用法 Thunk函数的含义与用法 co函数库的含义与用法 async函数的含义与用法 异步编程对 JavaScript 语言太重要.JavaScript 只有一根线程,如果没有异步编程,根本没法用,非卡死不可. 以前,异步编程的方法,大概有下面四种. 回调函数 事件监听 发布/订阅 Promise 对象 ECMAScript 6 (简称 ES6 )作为下一代 JavaScript 语言,将

javascript函数库:jQuery基础教程

文章简介:jQuery是目前使用最广泛的javascript函数库.据统计,全世界排名前100万的网站,有46%使用jQuery,远远超过其他库.微软公司甚至把jQuery作为他们的官方库.对于网页开发者来说,学会jQuery是必要的.因为它让你了解业界最通用的技术,为将来学习更高级的库打下基础,并 jQuery是目前使用最广泛的javascript函数库.据统计,全世界排名前100万的网站,有46%使用jQuery,远远超过其他库.微软公司甚至把jQuery作为他们的官方库.对于网页开发者来说

在PHP3中实现SESSION的功能(一、SESSION函数库:session.inc.php3)(转译)

session|函数 <?phpif (!isset($__session_inc__)){$__session_inc__=1;//require("cookie.inc.php3");# ------------------------------------------------------------------- # Session Management v1.0 21.6.1998 # (c) Wild Karl Heinz <kh.wild@wicom.at

网络安全工具开发函数库介绍

---[[ 前言 ]]-------------------------------------------- 本文主要介绍几个在UNIX系统平台上开发网络安全工具时最常用的library.此外还提供一些如何使用这些开发库进行网络安全工具开发的设计框架和流程.希望能和对网络安全工具开发有兴趣的朋友共同交流,互相促进. 众所周知,基于socket的网络编程已成为当今不可替代的编程方法.这种编程思想将网络通讯当作"文件"描述字进行处理,对这个"网络文件"(即 socke

PHP 正则表达式函数库(两套)_正则表达式

在PHP中有两套正则表达式函数库,两者功能相似,只是执行效率略有差异: 一套是由PCRE(Perl Compatible Regular Expression)库提供的.使用"preg_"为前缀命名的函数: 一套由POSIX(Portable Operating System Interface of Unix )扩展提供的(PHP默认).使用以"ereg_"为前缀命名的函数: PHP中,正则表达式有三个作用: 匹配,也常常用于从字符串中析取信息. 用新文本代替匹配

《深入理解Hadoop(原书第2版)》——3.5Hadoop作业中的第三方函数库

3.5Hadoop作业中的第三方函数库 到目前为止,在Mapper和Reducer类中只使用了标准Java函数库和Hadoop函数库.这些标准函数库包括了Hadoop发行版中的类库和标准Java类库(比如String.class). 可是,仅仅使用这些标准函数库不能够开发复杂的Hadoop作业程序,有时我们需要第三方库的支持.如前所述,Mapper和Reducer类中使用的这些库需要被发送并配置到集群中运行Mapper和Reducer实例的所有节点. 首先,你要编写一个跟介绍过的代码不同的使用第

oracle跨库查询dblink的用法实例详解_oracle

本文实例讲述了oracle跨库查询dblink的用法.分享给大家供大家参考,具体如下: 1.创建之前的工作 在创建dblink之前,首先要查看用户是否有相应的权限.针对特定的用户,使用 sqlplus user/pwd登录后,执行如下语句: 复制代码 代码如下: select * from user_sys_privs t where t.privilege like upper('%link%'); 在sys用户下,显示结果为: SYS CREATE DATABASE LINK NO SYS