【问题标题】:Understanding code flow with yield/generators使用 yield/generators 理解代码流
【发布时间】:2014-06-26 09:57:20
【问题描述】:

我已经阅读了几个使用 JavaScript 生成器 such as this one 的代码示例。我能想到的最简单的生成器使用块是这样的:

function read(path) {
    return function (done) {
        fs.readFile(path, "file", done);
    }
}

co(function *() {
    console.log( yield read("file") );
})();

这确实打印出file 的内容,但我的挂断是调用done 的地方。看起来,yield 是语法糖,用于将返回的内容包装在回调中并适当地分配结果值(至少在 co 的情况下,将错误参数扔给回调)。我对语法的理解正确吗?

当使用yield 时,done 是什么样的?

【问题讨论】:

  • 您指的是done 中使用的read(path) 吗?我认为它与yield 或生成器没有任何关系——它是co 库的一部分。

标签: javascript node.js generator yield co


【解决方案1】:

看起来,yield 是语法糖,用于将返回的内容包装在回调中并适当地分配结果值(至少在 co 的情况下,将错误参数扔给回调)

不,yield 不是语法糖。它是生成器的核心语法元素。当该生成器被实例化时,您可以运行它(通过在其上调用.next()),这将返回returned 或yielded 的值。当生成器是yielded 时,您可以稍后通过再次调用.next() 来继续它。 next 的参数将是 yield 表达式在生成器中返回的值。

co 的情况下,那些异步回调事物(和 other things)会“适当地”处理,因为您认为在异步控制流库中是自然的。

当使用 yield 时,done 是什么样子的?

article that you read 中的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 在运行时会从生成器中生成表达式 read("file") 的值。这成为ret.valgen.next() 的结果。为此,传递了 next 函数 - 一个回调,它将使用传递给它的 result 继续生成器。在您的生成器代码中,yield 表达式似乎返回了此值。

所发生的事情的“展开”版本可以这样写:

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
});

【讨论】:

  • @Noseratio:对不起,我不太明白你的意思。 “分配给.value”在哪里是必要的,为什么?从yield 表达式返回的不是传递给.next() 的参数吗?如果需要更正,请随时编辑我的帖子。
  • @Bergi 我还是有点困惑;实际的 done 功能是什么?这是传递给callasync 的内容吗? yield 将发出 ret2 的值?为什么?
  • @ExplosionPills: ret1.value/callasync 是接受回调的函数,它将以next 作为done 参数调用,是的。 yield 确实会发出 res(已传递给 .next())作为 console.log 参数 - 这就是生成器的工作方式。
【解决方案2】:

我发布了关于生成器如何工作的详细说明 here

如果没有co(未经测试),您的代码在简化形式中可能如下所示:

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() 异步迭代生成器对象。

【讨论】:

  • @ExplosionPills,当item.value(nextStep)被调用时,nextStep对应的是done,而item.value对应的是read(path)返回的函数,当后者被调用时在yield 语句旁边,在worker 生成器内。
猜你喜欢
  • 1970-01-01
  • 2013-07-05
  • 2021-07-10
  • 2019-01-08
  • 2019-04-04
  • 2013-04-06
  • 1970-01-01
  • 1970-01-01
  • 2021-03-15
相关资源
最近更新 更多