最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

node.js - Javascript yield from the function nested inside generator - Stack Overflow

programmeradmin0浏览0评论

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

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];
    }
}
Share Improve this question asked Dec 5, 2015 at 11:18 Dima SlivinDima Slivin 63910 silver badges19 bronze badges
Add a comment  | 

5 Answers 5

Reset to default 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;
}

you can use the yield * syntax.

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

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.

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

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)
发布评论

评论列表(0)

  1. 暂无评论