Javascript 奇怪的生成器 yield 子函数行为
Javascript strange generator yield sub function behavior
我在一个简单的项目中使用 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 的实际代码在模型 class 中,所以我不希望将它与控制器合并。
我在子测试函数上尝试了很多不同的方法,有或没有 yield。
怎么回事?
subtest1(conn, values)
是一个生成器。 yield
生成器对象不会执行其主体。也就是说,生成的生成器保持挂起状态,并且需要调用 next()
方法才能到达第一个 yield
。 代码示例 2 中没有对 next()
的显式或隐式调用,这就是未执行 conn.query(...)
的原因。
yield* subtest1(conn, values)
怎么样?来自链接页面:
The yield*
expression iterates over the operand and yields each value
returned by it.
它仍然会延迟执行subtest
。
另一种解决方案是将 subtest
变成常规函数,并将 return 变成 conn.query(...)
的结果(假设您只需要执行一个查询):
function subtest1(conn, values) {
return conn.query("INSERT INTO version SET ?", values);
}
yield subtest1(conn, values)
调用 subtest1(conn, values)
,其中 returns 是一个迭代器对象,然后从 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);
}
这个更简单的版本可能有助于区分 yield
和 yield*
:
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 done
请注意我们根本看不到任何 "b" 日志记录;那是因为你在 f1
之后看到的 {}
是通过调用 bar
.
返回的迭代器对象
如果我们只是在 yield bar();
行上添加 *
,将其变成 yield* bar()
,输出会发生变化:(live copy)
f
b1
b2
0
b2
1
b2
2
b done
f done
如果 yield*
的操作数是一个迭代器,yield*
对其进行迭代,yield
计算它的每个值。基本上是:
for (value of bar()) {
yield value;
}
我在一个简单的项目中使用 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 的实际代码在模型 class 中,所以我不希望将它与控制器合并。
我在子测试函数上尝试了很多不同的方法,有或没有 yield。
怎么回事?
subtest1(conn, values)
是一个生成器。 yield
生成器对象不会执行其主体。也就是说,生成的生成器保持挂起状态,并且需要调用 next()
方法才能到达第一个 yield
。 代码示例 2 中没有对 next()
的显式或隐式调用,这就是未执行 conn.query(...)
的原因。
yield* subtest1(conn, values)
怎么样?来自链接页面:
The
yield*
expression iterates over the operand and yields each value returned by it.
它仍然会延迟执行subtest
。
另一种解决方案是将 subtest
变成常规函数,并将 return 变成 conn.query(...)
的结果(假设您只需要执行一个查询):
function subtest1(conn, values) {
return conn.query("INSERT INTO version SET ?", values);
}
yield subtest1(conn, values)
调用 subtest1(conn, values)
,其中 returns 是一个迭代器对象,然后从 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);
}
这个更简单的版本可能有助于区分 yield
和 yield*
:
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 done
请注意我们根本看不到任何 "b" 日志记录;那是因为你在 f1
之后看到的 {}
是通过调用 bar
.
如果我们只是在 yield bar();
行上添加 *
,将其变成 yield* bar()
,输出会发生变化:(live copy)
f b1 b2 0 b2 1 b2 2 b done f done
如果 yield*
的操作数是一个迭代器,yield*
对其进行迭代,yield
计算它的每个值。基本上是:
for (value of bar()) {
yield value;
}