【问题标题】:Javascript strange generator yield sub function behaviorJavascript 奇怪的生成器 yield 子函数行为
【发布时间】:2016-03-19 03:11:09
【问题描述】:

我在一个简单的项目中使用 MySQL (mysql-co) 和 ASQ(asynquence) 来更好地理解 ES6 生成器和 yield 函数,但我被一个奇怪的行为难住了。

asynquence的简短解释

asynquence (https://github.com/getify/asynquence) 为我提供了一种按顺序运行生成器的简单方法。它也可以进行伪并行执行,但这不是我现在需要的。 function *x(token) 的结构来自那里。 token[0] 持有一个连接对象。 yield token 按顺序将控制权传递给下一个生成器函数。

代码示例 1(工作)

function *test1(token) {
  var conn = token.messages[0];
  var values = {id:1, dev:1, description:'This is it!'};
  yield conn.query("INSERT INTO version SET ?", values);
  yield token;
}

这很好用。上面描述的行被插入。我不知道 MySQL 驱动程序允许这么简单的插入函数,但确实如此。

代码示例 2(不起作用)

function *test1(token) {
  var conn = token.messages[0];
  var values = {id:1, dev:1, description:'This is it!'};
  yield subtest1(conn, values);
  yield token;
}
function *subtest1(conn, values) {
  yield conn.query("INSERT INTO version SET ?", values);
}

这不起作用。 subtest1 的实际代码在模型类中,所以我不希望它与控制器合并。

我在 subtest 函数上尝试了很多不同的方法,有或没有 yield。

发生了什么事?

【问题讨论】:

    标签: javascript mysql generator ecmascript-6 yield


    【解决方案1】:

    subtest1(conn, values) 是一个生成器。 yielding 生成器对象不会执行其主体。也就是说,产生的生成器保持挂起状态,它需要调用next() 方法才能到达第一个yield代码示例 2 中没有对 next() 的显式或隐式调用,这就是不执行 conn.query(...) 的原因。

    yield* subtest1(conn, values) 怎么样?从链接页面:

    yield* 表达式遍历操作数并产生每个值 由它返回。

    它仍然会懒惰地执行subtest

    另一种解决方案是将subtest 转换为常规函数并返回conn.query(...) 的结果(假设您只需要执行一个查询):

    function subtest1(conn, values) {
        return conn.query("INSERT INTO version SET ?", values);
    }
    

    【讨论】:

    • "yielding 生成器对象不会开始执行" 此处的措辞很棘手。它确实启动它,因为它调用函数并取回迭代器。只是它不会遍历它返回的内容,它只是为test1 的当前迭代生成实际的迭代器对象。 (尽管 +1)
    【解决方案2】:

    yield subtest1(conn, values) 调用subtest1(conn, values),它返回一个迭代器对象,然后从test1 产生它作为test1 的迭代值。它不会迭代 subtest1 迭代器返回的值。

    可以test1 通过在yield 之后添加* 来迭代来自subtest1 的迭代器的值:

    yield* subtest1(conn, values);
    

    但是查看您的代码,我认为您不想这样做。如果你想将conn.query 行拆分成一个函数,它看起来像这样:

    function *test1(token) {
      var conn = token.messages[0];
      var values = {id:1, dev:1, description:'This is it!'};
      yield subtest1(conn, values);
      yield token;
    }
    function subtest1(conn, values) {
      return conn.query("INSERT INTO version SET ?", values);
    }
    

    这个更简单的版本可能有助于区分yieldyield*

    function* foo() {
      console.log("f");
      yield bar();
      console.log("f done");
    }
    function* bar() {
      console.log("b1");
      let i = 0;
      while (i < 3) {
        console.log("b2");
        yield i;
        ++i;
      }
      console.log("b done");
    }
    for (let v of foo()) {
      console.log(v);
    }
    

    它的输出是:(live copy on Babel's REPL)

    F {} f 完成

    注意我们根本看不到任何“b”日志;那是因为f1后面看到的{}是调用bar返回的迭代器对象。

    如果我们只是在yield bar(); 行上添加*,将其转换为yield* bar(),输出会发生变化:(live copy)

    F b1 b2 0 b2 1 b2 2 b 完成 f 完成

    如果yield* 的操作数是一个迭代器,yield* 将对其进行迭代,yield 对其每个值进行迭代。基本上是:

    for (value of bar()) {
        yield value;
    }
    

    【讨论】:

    • 您添加的示例非常适合将概念带回家!
    猜你喜欢
    • 2019-08-07
    • 1970-01-01
    • 2014-07-08
    • 1970-01-01
    • 2023-03-23
    • 2021-09-05
    • 1970-01-01
    • 2011-10-25
    • 1970-01-01
    相关资源
    最近更新 更多