Why dosent fruit get assigned 'apple' in this generator function? [duplicate] - javascript

This question already has answers here:
console.log not being called from generator function
(1 answer)
Understanding generators in JavaScript
(3 answers)
Where argument of first next() call goes? [duplicate]
(2 answers)
ES6 yield : what happens to the arguments of the first call next()?
(1 answer)
In ES6, what happens to the arguments in the first call to an iterator's `next` method?
(3 answers)
Closed 1 year ago.
Title says it all.
Output is:
starting
lol
undefined
0 undefined
Expected:
starting // yield giving us back a string
lol // yield giving back result from bar()
'apples' // since the 'yield bar()' is 'replaced' by 'apples' via next('apples')
'0 apples' // the last yield should tell us what fruit is, but its undefined
Code:
const bar = () => 'lol'
function* foo1() {
yield 'starting'
let a = 0
let fruit = yield bar()
console.log(fruit) // <-- y no apples? I could even see 'lol' being here too.
yield `${a} ${fruit}`
}
let g = foo1()
console.log(g.next().value)
console.log(g.next('apples').value)
console.log(g.next().value)

According to Mozilla Docs function*
// the first call of next executes from the start of the function until the first yield statement
You must 'init' with a first next() call before getting yield statement
const bar = () => 'lol'
function* foo1() {
yield 'starting'
let a = 0
let fruit = yield bar()
console.log(fruit) // <-- y no apples? I could even see 'lol' being here too.
yield `${a} ${fruit}`
}
let g = foo1()
g.next();
console.log( g.next().value)
console.log( g.next('apples').value )

Related

how generator.next is causing an exception in this code

Having difficulty understanding this code.
function* gen() {
try {
let result = yield "2 + 2 = ?"; // (1)
alert("The execution does not reach here, because the exception is thrown above");
} catch(e) {
alert(e); // shows the error
}
}
let generator = gen();
let question = generator.next().value;
generator.throw(new Error("The answer is not found in my database")); // (2)
how this line (let question = generator.next().value;) influences the code, i mean generators only return yeilded values?
what does the author mean by (1) on the third line
The following statement
generator.next()
will yield the following object:
{
value: "2 + 2 = ?",
done: false
}
At this point, generator function is paused at current yield which is
let result = yield "2 + 2 = ?";
If you call generator.next() and pass any argument to the next() method, that argument will become the value of the yield expression and will be assigned to the result variable.
But instead of calling next(), you have called throw method which is like injecting a throw statement where the current yield is, i.e. at line (1).
Calling throw() throws an exception inside the try block which is then caught by the catch block.
throw method returns an object which, in your case, is:
{
value: undefined,
done: true
}
value is undefined because you didn't return or yield any value from the catch block.
For more details, see: MDN - Generator.prototype.throw()
what does the author mean by (1) on the third line
Author is probably trying to communicate that the generator function will pause at (1); after that calling the throw() method will throw an error inside the try block as if there was a throw statement at (1) which is then caught by the catch block; catch block then logs the error on the console.

javascript recursion vs while loop

quote from: https://github.com/javascript-tutorial/zh.javascript.info/blob/master/1-js/12-generators-iterators/1-generators/01-pseudo-random-generator/solution.md
function* pseudoRandom(seed) {
let value = seed;
while(true) {
value = value * 16807 % 2147483647
yield value;
}
};
let generator = pseudoRandom(1);
alert(generator.next().value); // 16807
alert(generator.next().value); // 282475249
alert(generator.next().value); // 1622650073
question: https://github.com/javascript-tutorial/zh.javascript.info/blob/master/1-js/12-generators-iterators/1-generators/01-pseudo-random-generator/task.md
My solution:
function* pseudoRandom(seed) {
const value = seed * 16807 % 2147483647
yield value
yield* pseudoRandom(value)
};
let generator = pseudoRandom(1);
alert(generator.next().value); // 16807
alert(generator.next().value); // 282475249
alert(generator.next().value); // 1622650073
What's the difference,thanks.
It depend on how long you are planning to run this program. Your loop might go forever but the recursion one will throw an error.
All the function calls you do are placed in a stack. At some point, your stack is going to be full, causing a Stackoverflow Exception.
The stack limit depends on the browser, but pass that limit, the Recursion one will stop in an error while the loop will continue.

How does Generator.next() processes its parameter?

The documentation says that "You can also provide a parameter to the next method to send a value to the generator." Where does it sends it to?
For example, take these 3 generators:
function* one() {
while(true) {
var value = yield null;
}
}
var g1 = one(); g1.next();
g1.next(1000); //yields null
function* two() {
var i = 0;
while (true) {
i += yield i;
}
}
var g2 = two(); g2.next();
g2.next(1000) // yields 1000
function* three(){
var index = 0;
while (true)
yield index++;
}
g3 = three();
g3.next();
g3.next(1000); // yields 1
In generators 3 and 1, the argument passed has no effect on next. Why is that? How does generator 2 calculates its return value? Why it is affected by the given argument?
The key to understanding this is knowing how the next function retrieves the argument passed to next(), which is as the return value of the yield operator:
[rv] = yield [expression];
Independently of the value of [expression], yield will assign to rv the value passed to next().
But, here comes the tricky part: yield will only assign the value passed to next() when resuming execution from a previous iteration. As a consequence, on the first iteration, yield does not assign anything to rv.
For example, if I have this generator:
function* gen() {
// On the first iteration, yield does not return anything.
//because it returns something ONLY when execution is resumed
returnedFromYield = yield 'foo';
yield returnedFromYield;
}
returnedFromYield is undefined on the first iteration. When execution is resumed on the second iteration, yield assigns the passed value to the returnedFromYield variable, which is then returned:
g.next(1); // 'foo'
g.next(2); // 2
Let's review another example:
function* gen() {
yield yield yield 5;
}
On the first iteration, (g.next()), yield will return 5, on the second iteration, (g.next(10)) yield is going to pass 10 to the second yield. That is, yield yield yield 5; on the second iteration is equivalent to yield yield 10;, and, on the third iteration, it's equivalent to yield valuePassedToNext.
Here's an annotated example showing the logical flow and scope for the values passed to a generator's next method. The affected area of the next method call is highlighted in the corresponding color.
a: There is no yield, so argument 'one' is ignored. The generator pauses on line 5, returning the value 1.
b: Execution resumes and 'two' replaces yield on line 5, and is assigned to the yieldValue variable. Line 6 is executed. The generator pauses on line 8, returning the value 2.
c: Execution resumes and 'three' replaces yield on line 8, and is assigned to the yieldValue variable. Line 9 is executed. The generator pauses on line 11, returning the value 3.
d: Execution resumes and 'four' replaces yield on line 11, and is assigned to the yieldValue variable. Line 12 is executed. Line 14 is executed. Finally the generator is now done, returning the value undefined.
See the logs on line 28-36

ES6 yield (yield 1)(yield 2)(yield 3)()

function* generatorFunction() {
yield (yield 1)(yield 2)(yield 3)();
}
var iterator = generatorFunction();
// [1, 2, 3]
var iteratedOver = [iterator.next().value, iterator.next().value, iterator.next().value];
I'm not sure how this works.
yield doesn't return a function reference, so what are the parenthetical statements like (yield 2) doing - are they fat arrow anonymous functions without bodies? How are they called using partial application like that?
I'm missing something here, can someone explain?
Update: Tried on three browsers, Chrome 50.0.2661.86, Safari 9.1 (50.0.2661.86), Firefox 44.0.2, all perform without errors.
ESFiddle also executes it without errors.
Commenters report Babel executes without errors as well.
The source of the question is from http://tddbin.com/#?kata=es6/language/generator/send-function, the second kata.
I'm not sure how this works.
Uh, yeah, it shouldn't work. It's only working because of a bug in Babel.
yield doesn't return a function reference, so what are the parenthetical statements like (yield 2) doing - are they fat arrow anonymous functions without bodies? How are they called using partial application like that?
No, it's really just standard function application, no magic. yield could return a function reference, and when it does this might work. When it doesn't, it will throw an exception on the third .next() call.
As an example for a working version:
function* generatorFunction() {
yield (yield 1)(yield 2)(yield 3)();
}
var test = (a) => {
console.log(a);
return (b) => {
console.log(b);
return (c) => {
console.log(c);
return 4;
};
};
};
var iterator = generatorFunction();
iterator.next(); // {value: 1, done: false}
iterator.next(test); // {value: 2, done: false}
iterator.next("a"); // "a" {value: 3, done: false}
iterator.next("b"); // "b" undefined {value: 4, done: false}
iterator.next("d"); // {value: undefined, done: true}
So how does this work? Those nested/chained yield statements should better be written as
function* generatorFunction() {
let fn1 = yield 1;
let a = yield 2;
let fn2 = fn1(a);
let b = yield 3;
let fn3 = fn2(b);
let res = fn3();
let d = yield res;
return undefined;
}
Commenters report Babel executes without errors as well.
That's because of a babel bug. If you check the transpiler output, it actually behaves like
function* generatorFunction() {
let fn1 = yield 1;
let a = yield 2;
let b = yield 3;
// these are no more executed with only 3 `next` calls
let fn2 = fn1(a);
let fn3 = fn2(b);
let res = fn3();
let d = yield res;
return undefined;
}

Javascript strange generator yield sub function behavior

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

Categories

Resources