使用 yield/generator 理解代码流

IT技术 javascript node.js generator yield co
2021-02-01 12:36:55

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

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

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

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

是什么done样子的时候喜欢yield用的?

2个回答

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

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

只有在这种情况下co,那些异步回调事物(和其他事物)才会“适当地”处理您认为在异步控制流库中很自然的事情。

使用 yield 时 done 是什么样的?

您阅读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.val,的结果gen.next()为此,next函数被传递 - 一个回调,它将使用res传递给它ult继续生成器在您的生成器代码中,看起来好像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
});
@ExplosionPills: ret1.value/callasync是接受回调的函数,它将next作为done参数被调用,是的。yield不发射res(这是传入.next())的console.log说法-这是发电机的工作原理。
2021-03-16 12:36:55
@Noseratio:对不起,我不太明白你的意思。“分配给.value在哪里是必要的,为什么?yield表达式返回的内容不是传递给的参数.next()吗?如果需要更正,请随时编辑我的帖子。
2021-03-25 12:36:55
@Bergi 我还是有点困惑;实际完成的功能是什么?那是传进去的callasync吗? yield会发出ret2? 为什么?
2021-03-27 12:36:55

我在这里发布了有关生成器如何工作的详细说明

在简化形式中,您的代码可能如下所示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对应于doneitem.value对应于返回的函数read(path),当后者在yield语句旁边被调用时,在worker生成器内部
2021-04-09 12:36:55