Javascript strange generator yield sub function behavior

╄→尐↘猪︶ㄣ 提交于 2019-12-13 15:12:20

问题


I'm using MySQL (mysql-co) and ASQ(asynquence) in a simple project to get a better understanding of ES6 generators and yield functions, and I'm stumped on an odd behavior.

Short explanation of asynquence

asynquence (https://github.com/getify/asynquence) provides an easy way for me to run generators in sequence. It can also do pseudo-parallel execution but that's not what I need for now. The structure of function *x(token) is from there. token holds a connection object at [0]. yield token passes control on to the next generator function in sequence.

Code Sample 1 (works)

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

This works fine. The row described above gets inserted. I didn't know the MySQL driver allowed such a simple looking insert function but it does.

Code Sample 2 (doesn't work)

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

This doesn't work. The actual code in question for subtest1 is in a model class, so I would prefer not to have it merged in with the controller.

I've tried a bunch of different things around with or without yield on the subtest function.

What's going on?


回答1:


subtest1(conn, values) is a generator. yielding a generator object does not execute its body. That is, the yielded generator remains suspended, and it would require a call to the next() method for the first yield to be reached. There are no explicit or implicit calls to next() in Code Sample 2, and this is the reason conn.query(...) isn't executed.

How about yield* subtest1(conn, values)? From the linked page:

The yield* expression iterates over the operand and yields each value returned by it.

It will still execute subtest lazily.

An alternative solution is to turn subtest into a regular function and return the result of conn.query(...) (assuming you only need to perform one query):

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



回答2:


yield subtest1(conn, values) calls subtest1(conn, values), which returns an iterator object, and then yields that from test1 as the value for that iteration of test1. It doesn't iterate the values returned by the subtest1 iterator.

You could have test1 iterate the values from subtest1's iterator by adding a * after the yield:

yield* subtest1(conn, values);

but looking at your code, I don't think you want to. If you want to split the conn.query line out into a function, it would look like this:

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

This simpler version may help with the difference between yield and 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);
}

The output of that is: (live copy on Babel's REPL)

f
{}
f done

Note how we don't see any of the "b" logging at all; that's because the {} you see after f1 is the iterator object returned by calling bar.

If we just add * on the yield bar(); line, turning it into yield* bar(), the output changes: (live copy)

f
b1
b2
0
b2
1
b2
2
b done
f done

If the operand to yield* is an iterator, yield* iterates it, yielding each of its values. It's basically:

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


来源:https://stackoverflow.com/questions/34253463/javascript-strange-generator-yield-sub-function-behavior

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!