Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

forEach using generators in Node.js

I'm using Koa.js framework and Mongoose.js module.

Normally to get a result from MongoDB I code like this:

var res = yield db.collection.findOne({id: 'my-id-here'}).exec();

But I need to execute this line for every element of an array named 'items'.

items.forEach(function(item) {
  var res = yield db.collection.findOne({id: item.id}).exec();
  console.log(res)  // undefined
});

But this code doesn't run as yield is in the function. If I write this:

items.forEach(function *(item) {
  var res = yield db.collection.findOne({id: item.id}).exec();
  console.log(res)  // undefined
});

I'm not getting the result in res variable either. I tried to use 'generator-foreach' module but that didn't worked like this.

I know that this is my lack of knowledge about the language literacy of Node.js. But can you guys help me finding a way how to do this?

like image 234
Mazhar Ahmed Avatar asked Jul 16 '14 08:07

Mazhar Ahmed


People also ask

What are generators in Nodejs?

Generators are function executions that can be suspended and resumed at a later point. Generators are useful when carrying out concepts such as 'lazy execution'. This basically means that by suspending execution and resuming at will, we are able to pull values only when we need to.

What is the difference between map () and forEach ()?

forEach “executes a provided function once per array element.” Array. map “creates a new array with the results of calling a provided function on every element in this array.”

Can forEach be chained?

map() is chainable, and can pass its new array to another chained method. forEach() is not chainable.

Which is better forEach or map?

The main difference between map and forEach is that the map method returns a new array by applying the callback function on each element of an array, while the forEach method doesn't return anything. You can use the forEach method to mutate the source array, but this isn't really the way it's meant to be used.


3 Answers

You can yield arrays, so just map your async promises in another map

var fetchedItems = yield items.map((item) => {
   return db.collection.findOne({id: item.id});
});
like image 132
Umidbek Karimov Avatar answered Sep 20 '22 18:09

Umidbek Karimov


The accepted answer is wrong, there is no need to use a library, an array is already an iterable.

This is an old question, but since it has no correct answer yet and it appears on the first page on google search for the key terms "iterators and forEach" I will respond the question:

There is no need to iterate over an array, since an array already conforms to the iterable API.

inside your generator just use "yield* array" (note the * ) yield* expression is used to delegate to another generator or iterable object

Example:

let arr = [2, 3, 4];

    function* g2() { 
      yield 1;
      yield* arr;
      yield 5;
    }

    var iterator = g2();

    console.log(iterator.next()); // { value: 1, done: false }
    console.log(iterator.next()); // { value: 2, done: false }
    console.log(iterator.next()); // { value: 3, done: false }
    console.log(iterator.next()); // { value: 4, done: false }
    console.log(iterator.next()); // { value: 5, done: false }
    console.log(iterator.next()); // { value: undefined, done: true }

For examples and in depth information visit: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/yield*

like image 37
Jorge Riv Avatar answered Sep 22 '22 18:09

Jorge Riv


Thanks guys, I've done this using the 'CO' module. Thanks.

var co = require('co');

items.forEach(co(function* (item) {
  var img = yield db.collection.findOne({id: item.id}).exec();
}));

EDIT: With the latest version of CO, you need co.wrap() for this to work.

like image 24
Mazhar Ahmed Avatar answered Sep 22 '22 18:09

Mazhar Ahmed