在 JavaScript 中,生成器函数中的“return someValue”是反模式吗?

nop*_*ole 5 javascript anti-patterns generator ecmascript-6

下面.next()可以显示最后一个值{ value: 3, done: true }

function* genFn() {
  yield 1;
  yield 2;
  return 3;
}

const iter = genFn();
console.log(iter.next());
console.log(iter.next());
console.log(iter.next());
Run Code Online (Sandbox Code Playgroud)

但如果用作可迭代则不然:

function* genFn() {
  yield 1;
  yield 2;
  return 3;
}

const iter = genFn();
console.log([...iter]);
Run Code Online (Sandbox Code Playgroud)

似乎无论使用return value或不return使用,意思是return undefined,如果它是可迭代协议,则不使用该值,因此也是迭代器协议。

Ry-*_*Ry- 3

\n

我认为换句话说,就是return someValue生成器函数是反模式吗?

\n
\n\n

不,但您应该只在有意义时才使用它。除了.next()手动调用外,yield*还会产生它。

\n\n

\r\n
\r\n
function* a() {\r\n    yield 1;\r\n    yield 2;\r\n    return 3;\r\n}\r\n\r\nfunction* b() {\r\n    console.log(yield* a());\r\n}\r\n\r\nconsole.log([...b()]);
Run Code Online (Sandbox Code Playgroud)\r\n
\r\n
\r\n

\n\n

一个非常实际的例子是预异步函数,它yield可以用作await并且您\xe2\x80\x99d 仍然想返回一个值。当基于 Promise/thenables 编写类似的 \xe2\x80\x99t 模式时,相同的概念仍然适用。

\n\n

不受 JavaScript 调用堆栈限制的递归,例如:

\n\n

\r\n
\r\n
function* sillyAdd(a, b) {\r\n    return b === 0\r\n        ? a\r\n        : yield sillyAdd(a + 1, b - 1);\r\n}\r\n\r\nconst restack = f => (...args) => {\r\n    const stack = [f(...args)];\r\n    let ret = undefined;\r\n\r\n    while (stack.length !== 0) {\r\n        let {value, done} = stack[stack.length - 1].next(ret);\r\n\r\n        if (done) {\r\n            stack.pop();\r\n            ret = value;\r\n        } else {\r\n            stack.push(value);\r\n        }\r\n    }\r\n\r\n    return ret;\r\n};\r\n\r\nconsole.log(restack(sillyAdd)(2, 100000));\r\nconsole.log(\'(it\xe2\x80\x99s synchronous)\');
Run Code Online (Sandbox Code Playgroud)\r\n
\r\n
\r\n

\n\n

通过在暂停函数中保持状态来进行在线解析:

\n\n

\r\n
\r\n
function* isBalanced() {\r\n    let open = 0;\r\n\r\n    for (let c; c = yield;) {\r\n        if (c === \'(\') {\r\n            open++;\r\n        } else if (c === \')\') {\r\n            open--;\r\n\r\n            if (open < 0) {\r\n                return false;\r\n            }\r\n        }\r\n    }\r\n\r\n    return open === 0;\r\n}\r\n\r\nclass Parser {\r\n    constructor(generator) {\r\n        this.generator = generator();\r\n        const initial = this.generator.next();\r\n        this.done = initial.done;\r\n        this.result = initial.value;\r\n    }\r\n\r\n    write(text) {\r\n        if (this.done) {\r\n            return;\r\n        }\r\n\r\n        for (const c of text) {\r\n            const {value, done} = this.generator.next(c);\r\n\r\n            if (done) {\r\n                this.done = true;\r\n                this.result = value;\r\n                break;\r\n            }\r\n        }\r\n    }\r\n\r\n    finish() {\r\n        if (this.done) {\r\n            return this.result;\r\n        }\r\n\r\n        const {value, done} = this.generator.next();\r\n\r\n        if (!done) {\r\n            throw new Error(\'Unexpected end of input\');\r\n        }\r\n\r\n        return value;\r\n    }\r\n}\r\n\r\nconst p = new Parser(isBalanced);\r\n\r\n// the product of these could be too large to fit in memory\r\nconst chunk = \'()\'.repeat(1000);\r\n\r\nfor (let i = 0; i < 100; i++) {\r\n    p.write(chunk);\r\n}\r\n\r\nconsole.log(p.finish());
Run Code Online (Sandbox Code Playgroud)\r\n
\r\n
\r\n

\n

  • 有趣的 `yield* a()` 会产生它,但它有什么用呢? (2认同)