让人头痛的Generator 函数的异步应用真的有用吗?( 五 )
var g = gen();var r1 = g.next();r1.value(function (err, data) {if (err) throw err;var r2 = g.next(data);r2.value(function (err, data) {if (err) throw err;g.next(data);});});
上面代码中 , 变量g是 Generator 函数的内部指针 , 表示目前执行到哪一步 。 next方法负责将指针移动到下一步 , 并返回该步的信息(value属性和done属性) 。
仔细查看上面的代码 , 可以发现 Generator 函数的执行过程 , 其实是将同一个回调函数 , 反复传入next方法的value属性 。 这使得我们可以用递归来自动完成这个过程 。
Thunk 函数的自动流程管理Thunk 函数真正的威力 , 在于可以自动执行 Generator 函数 。 下面就是一个基于 Thunk 函数的 Generator 执行器 。
function run(fn) {var gen = fn();function next(err, data) {var result = gen.next(data);if (result.done) return;result.value(next);}next();}function* g() {// ...}run(g);
上面代码的run函数 , 就是一个 Generator 函数的自动执行器 。 内部的next函数就是 Thunk 的回调函数 。 next函数先将指针移到 Generator 函数的下一步(gen.next方法) , 然后判断 Generator 函数是否结束(result.done属性) , 如果没结束 , 就将next函数再传入 Thunk 函数(result.value属性) , 否则就直接退出 。
有了这个执行器 , 执行 Generator 函数方便多了 。 不管内部有多少个异步操作 , 直接把 Generator 函数传入run函数即可 。 当然 , 前提是每一个异步操作 , 都要是 Thunk 函数 , 也就是说 , 跟在yield命令后面的必须是 Thunk 函数 。
var g = function* (){var f1 = yield readFileThunk('fileA');var f2 = yield readFileThunk('fileB');// ...var fn = yield readFileThunk('fileN');};run(g);
上面代码中 , 函数g封装了n个异步的读取文件操作 , 只要执行run函数 , 这些操作就会自动完成 。 这样一来 , 异步操作不仅可以写得像同步操作 , 而且一行代码就可以执行 。
Thunk 函数并不是 Generator 函数自动执行的唯一方案 。 因为自动执行的关键是 , 必须有一种机制 , 自动控制 Generator 函数的流程 , 接收和交还程序的执行权 。 回调函数可以做到这一点 , Promise 对象也可以做到这一点 。
5、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 对象 。 如果数组或对象的成员 , 全部都是 Promise 对象 , 也可以使用 co , 详见后文的例子 。
推荐阅读
- 雷军发布会上的话!让人看到了雷军的“另一面”!网友:有良心
- 机器人送雪球!哈尔滨这场专业“打雪仗”,太让人羡慕了
- 2021年的中兴有多让人期待?倪飞在新年贺词中表了态
- XSX主机散热、噪声实机测试 出色表现让人满意
- 华为 WATCH FIT 体验:只有 34 克重,一款愿意让人戴着睡觉的智能手表
- 华为越南代言人走红,手机好不好用不清楚,让人挪不开眼睛
- 微信右上角+号隐藏5个黑科技功能,一键开启,让人相见恨晚
- 支付宝和微信支付,到底哪个使用人数更多呢?答案让人比较意外
- iPhone 11再度降价,直降700让人觉得有些太不保值
- iPhone12来了PVD工艺让人亮眼