3

So if you have a function like this:

function* queryDB(query){
    yield db.collection.find(query);
}

From what I understand, it will yield what db.collection.find() returns, so say it returns a promise, then queryDB() should return an iterable with a promise as a value in it, so something like co can be used to consolidate promises yielded into a single promise. Is there a way to yield what would be passed to that promise, without modifying db.collection.find() so that I can just use a for-of loop to iterate over the results from the database?

TO BE CLEAR

I am asking if I can get the arguments that are passed to the function passed into the .then() of a promise. As in

queryDB().then(function(<values>)

but instead I yield an iterator of the <values>.

  • [No](http://stackoverflow.com/a/29281361/1048572) – Bergi Jan 22 '16 at 16:10
  • `for...of` is synchronous. Asynchronous methods cannot be processed synchronously. – Felix Kling Jan 22 '16 at 16:22
  • ... ummm I don't think you read my question right... I can *iterate over the result* of the generator. –  Jan 25 '16 at 14:34
  • 1
    @Bergi I'm asking something rather different than what you claim is a duplicate. I'm asking to get the values from db.collection.find() as oppose to wrapping promises. –  Jan 25 '16 at 14:37
  • If you mean that you want to use `for (value of queryDB())`, then that sounds exactly like the duplicate (if not, please [edit] your question and add the code that you'd expect to use). It's impossible to unwrap promises and make them synchronous. You can only use `then`, or use `yield` inside a generator that is run by `co` or similar. – Bergi Jan 25 '16 at 14:44
  • @Bergi did you not read " Is there a way to yield what would be passed to that promise, without modifying db.collection.find() so that I can just use a for-of loop to iterate over the results from the database?" Keywords being passed to the promise. I think it's pretty clear. –  Jan 25 '16 at 15:06
  • @DaveWallace: Sorry, no, it's not clear to me, as you cannot "pass" anything to a promise - a promise is a value, not a function. So without knowing what you want to yield, I don't understand the first half of that sentence, and the second half sounds like a dupe of the questions I linked. – Bergi Jan 25 '16 at 15:17
  • @Bergi it's pretty clear you do understand you're just being uptight about it. But whatever, this is why SO is on a downturn, people abusing their power. But I edited it anyway. –  Jan 25 '16 at 15:43
  • @Bergi sounds like you're being a little harsh on this guy, I understood this quite clearly, and I agree it is not a dupe. – Christian Grabowski Jan 25 '16 at 15:44
  • @DaveWallace: So you want to yield the result values of the promise, ok. Then the answer is *no* (as outlined in the linked posts), as those results come in asynchronously and you cannot access them without waiting. – Bergi Jan 25 '16 at 15:58
  • @ChristianGrabowski: Sorry if I came off as harsh, but I really think he's asking for the same thing as the linked questions. Regardless, I've reopened the question so you may post your answer. – Bergi Jan 25 '16 at 16:00
  • 1
    You want to get the values passed as arguments to the `then` callback. What I'm not sure I understand: Where do you want them? inside the generator (`queryDB`), so you can yield them? Or do you want the aformentioned `then` callback to yield an interator of the values? (I don't actually have a solution or anything, I just don't fully understand the question) – nils Jan 25 '16 at 16:54
  • 1
    You should write how you want to use `queryDB`. How interface will look like? – Dmitry Yaremenko Jan 25 '16 at 20:04
  • @nils the first option is what I want. –  Jan 25 '16 at 22:38
  • @DaveWallace so, inside generator queryDb? – Dmitry Yaremenko Jan 26 '16 at 04:30
  • @DmitryYaremenko I am asking: Is it possible to return an iterator that contains the arguements thatdb.collection.find() returns. So probably an error arguement and the result from the database. Which is to say, I *am not yielding the promise being returned from db.collection.find()* –  Jan 27 '16 at 15:43
  • @DaveWallace `function* queryDB(query) { db.collection.find(query).then(function(values) { yield values; }); }` - something like that ? No. (added case in the answer) – Dmitry Yaremenko Jan 27 '16 at 15:50

1 Answers1

2

You can do this only inside generator.

function* queryDB(query) {
  // Here is synchrous-like code!
  var rows = yield db.collection.find(query);

  var sum = 0;

  // Use the rows synchrously
  for (var x of rows) {
    sum += x;
  }

  return sum;
}

var sumPromise = doM(queryDB('query example'));

sumPromise.then(console.log.bind(console));

See demo (I recommend to play with this and see what it returns in different cases and it will be clear).


And you can't do:

for (var x of db.collection.find(query))
  ...

Or:

for (var x of queryDB('some query'))
  ...

It will be like:

for (var x of (new Promise(...)))
  ...

Your generator / wrapper will return a promise. You can do only then with it.


And you can not do:

function* queryDB(query) {
  db.collection.find(query).then(function(values) {
      yield values;
  });
}

Explanation of the last:

How compiler will see this code:

function* queryDB(query) {
  db.collection.find(query).then(anonymousFunction);
}

And anonymousFunction is not a generator, so you can't write yield there. Also, Promise in then doesn't expect a generator.

On the other side if you want to use for (var x in queryDB()) compiler expects to return an array from queryDB(). If your queryDB() is asynchrous it will return a Promise-like interface.

If you switch to more functional style, you can write:

 forEach(queryDB(), function(item) {
    // action on item
 });

You can do reduce or map implementation if you now that your promised value is an array. But in this case you don't need a generator.


doM implementation:

function doM(gen) {
  function step(value) {
    var result = gen.next(value);
    if (result.done) {
      return result.value;
    }
    return result.value.then(step);
  }
  return step();
}

Source: Monads in JavaScript.

forEach implementation:

function forEach(promise, callback) {
  return promise.then(function(iterable) {
    for (var x of iterable) {
      callback(x);
    }
  });
}
Dmitry Yaremenko
  • 2,542
  • 20
  • 31
  • 1
    That's exactly what the OP is already doing. Only by using `co`, instead of a hand-written `doM`. – Bergi Jan 22 '16 at 16:12
  • yes, I'm asking how can I yield the arguements passed into that promise. –  Jan 25 '16 at 14:35
  • Edited an answer, not sure if I correctly understood the question. Hope this edition will be more helpful to explain the technique. Agree with Bergi that `for x of queryDB()` is not possible outside the generator (wrapper). – Dmitry Yaremenko Jan 25 '16 at 20:24
  • Can you just condense this to `function* queryDB(query) { db.collection.find(query).then(function(values) { yield values; }); }` not being possible? –  Feb 03 '16 at 01:26
  • @DaveWallace added to the answer – Dmitry Yaremenko Feb 03 '16 at 05:57