26

This code generates an error:

function *giveNumbers() {
 [1, 2, 3].forEach(function(item) {
 yield item;
 })
}

This is probably because yield is inside a function that is not a generator. Is there an elegant way to overcome this? I mean other than:

function *giveNumbers() {
 let list = [1, 2, 3];
 for (let i = 0; i < list.length; i++) {
 yield list[i];
 }
}
asked Dec 5, 2015 at 11:18

5 Answers 5

14

This is probably because yield is inside a function that is not a generator.

Yes. You cannot use yield from callbacks.

Is there an elegant way to overcome this?

Depends on the use case. Usually there is zero reason to actually want to yield from a callback.

In your case, you want a for...of loop, which is superior to .forEach in almost every aspect anyway:

function *giveNumbers() {
 for (let item of [1, 2, 3])
 yield item;
}
answered Dec 5, 2015 at 15:10
Sign up to request clarification or add additional context in comments.

8 Comments

Ceterum censeo forEach delendam esse a ES6.
For those of you who do not know Latin (like me) or are too lazy to google-translate: "forEach will certainly be wiped out in ES6".
@Palisand Furthermore, I consider that forEach must (should) be wiped out as of ES6
@Palisand Admittedly, that's at least what I meant, my Latin is a bit rusty and I have no idea whether "a ES6" is the right choice here
@Bergi - "there is zero reason to actually want to yield from a callback" - reason is to make asynchronous code synchronous. Why? To make it looks better. Is there some bypass to achieve this?
|
6

you can use the yield * syntax.

function *giveNumbers() {
 yield * [1, 2, 3].map(function(item) {
 return item;
 })
}
answered Apr 8, 2018 at 12:41

1 Comment

this does not yield to parent scope
1

yield returns the result to the caller.
let's assume the forEach callback is a generator (it's not a problem to set a costume generator there) - it means tha when the callback yield the result - it yields it back to forEach.

Basically, in your question what you attemp to do is:

callback -> yields to forEach -> yields to giveNumbers -> yields to caller

So, forEach should yield the result back to giveNumbers. but since forEach doesn't work like this, it's impossible without re-prototype arrays with costume forEach.

Actually, you second snippet is the most elegant to begin with.

answered Dec 5, 2015 at 11:46

Comments

0

You can also use while and pass arguments as such (Demo)

function *giveNumbers(array, start) {
 var index = start || 0;
 while(array.length > index + 1) {
 yield array[index++];
 }
 return array[index];
}
var g = giveNumbers([1,2,3], 0);
var finished = false;
while(!finished) {
 var next = g.next();
 console.log(next.value);
 if(next.done) {
 finished = true;
 }
}
answered Aug 26, 2016 at 10:54

Comments

0

Sometimes there's no way to yield directly like with the .map example in the other answer. For example, if we have a tree of nodes, and each node has a traverse method to traverse the tree, accepting a callback that runs for each node, we can create an additional array to yield:

function nodes(obj) {
 const nodes = []
 obj.traverse(node => nodes.push(node))
 yield * nodes
}
for (const node of nodes(someTree)) console.log(node)

If we have access to the array, we can directly loop to avoid an extra array:

function nodes(obj) {
 yield obj
 for (const node of obj.children) {
 yield * nodes(node)
 }
}
for (const node of nodes(someTree)) console.log(node)
answered Feb 16, 2024 at 9:09

Comments

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.