通过yield / generator来理解代码stream
我已经阅读了几个使用JavaScript生成器的代码示例, 比如这个 。 我能想到的最简单的发电机组块就是这样的:
function read(path) { return function (done) { fs.readFile(path, "file", done); } } co(function *() { console.log( yield read("file") ); })();
这确实打印出file
的内容,但是我的挂断是在done
调用的地方。 看起来,yield是语法糖,用于包装它在callback中返回的内容,并适当地分配结果值(至less在co
的情况下,将错误参数引发到callback函数中)。 我对语法的理解是否正确?
使用yield
时的yield
是什么?
看起来,yield是句法糖,用于包装它在callback中返回的内容,并适当地分配结果值(至less在co的情况下,将错误参数抛出callback)
不, yield
不是语法糖。 这是生成器的核心语法元素。 当这个生成器被实例化时,你可以运行它(通过调用.next()
),并且返回return
或者yield
的值。 当发生器yield
,可以通过再次调用.next()
继续。 接下来的参数将是yield
expression式在生成器内返回的值。
只有在co
情况下,那些asynchronouscallback的东西(以及其他东西 )才会被“适当地”处理,以便在asynchronous控制stream库中认为是自然的。
使用收益率时的结果是什么?
您阅读的文章中的thread
函数示例为您提供了一个很好的印象:
function thread(fn) { var gen = fn(); function next(err, res) { var ret = gen.next(res); if (ret.done) return; ret.value(next); } next(); }
在你的代码中, yield
在运行时产生expression式read("file")
的值。 这成为了ret.val
, gen.next()
的结果。 为此,传递next
函数 – 一个callback函数,它将继续传递给它的分辨率的生成器。 在您的生成器代码中,看起来好像yield
expression式返回了这个值。
发生的事情的“展开”版本可以这样写:
function fn*() { console.log( yield function (done) { fs.readFile("filepath", "file", done); } ); } var gen = fn(); var ret1 = gen.next(); var callasync = ret1.value; callasync(function next(err, res) { var ret2 = gen.next(res); // this now does log the value ret2.done; // true now });
我发布了发电机在这里工作的详细说明。
以简化forms,您的代码可能看起来像这样没有co
(未经testing):
function workAsync(fileName) { // async logic var worker = (function* () { function read(path) { return function (done) { fs.readFile(path, "file", done); } } console.log(yield read(fileName)); })(); // driver function nextStep(err, result) { try { var item = err? worker.throw(err): worker.next(result); if (item.done) return; item.value(nextStep); } catch(ex) { console.log(ex.message); return; } } // first step nextStep(); } workAsync("file");
workAsync
的驱动程序部分通过调用nextStep()
asynchronous地遍历生成器对象。