Can we pass parameters to a generator when we iterate it via for..of?

504 views Asked by At

I am thinking about a scenario of building up a promise queue:

//Let's assume that promises is an array of promises
var promiseQueue = [];
for (var promise of promises) {
    if (promiseQueue.length) promiseQueue[promiseQueue.length - 1].then(promise);
    promiseQueue.push(promise);
}

I am thinking about implementing a function called resolver:

function *resolve() {
    var promise;
    while (promise = yield) Promise.resolve(promise);
}

and then iterating it:

var promiseGenerator = resolve();

The problem is the for..of here which would be responsible for the actual iteration:

for (var r of promiseGenerator) {

}

At the code above the generator will be successfully iterated, but unfortunately I am not aware of a way to successfully pass a parameter to this generator at the iteration of for..of.

I would like to clarify that I do not need an alternative, I am perfectly aware that we can do something like this:

for (var p in promiseQueue) promiseGenerator.next(promiseQueue[p]);

I am specifically interested to know whether I can pass parameters to the generator when I execute a for..of cycle.

EDIT

The problem raised by amn is that in the example he/she was focusing on would always get undefined. That's true if we pass undefined to next(), but not true if we pass something else. The problem I was raising is that a for..of loop does not allow us to pass anything to yield, which is this specific question is all about, the example is a mere illustration of the problem, showing that the promises we would create will never be created in a for..of loop. However, there is life for Iterable objects outside the realm of for..of loops and we can pass defined values into the yield. An example with the criticized code chunk can look like:

function *resolve() {
    var promise;
    while (promise = yield) Promise.resolve(promise);
}

var responses = [];
var f = resolve();
var temp;
for (var i = 10; !(temp = f.next(i)).done; i--) responses.push(temp);

As we can see above, the yield above cannot be assumed ab ovo to be undefined. And of course we can pass some custom thenables, like

Promise.resolve({ 
  then: function(onFulfill, onReject) { onFulfill('fulfilled!'); }
});

or even promises which were not resolved yet. The point of the example was to show that we cannot pass values to the yield using the for..of loop, which is quite a feature gap in my opinion.

1

There are 1 answers

1
loganfsmyth On BEST ANSWER

No, it is not possible to pass arguments to next.

function* generateItems() { /* ... */ }
for (var item of generateItems()) {
   console.log(item);
}

is mostly short for

function* generateItems() { /* ... */ }
var iterator = generateItems()[Symbol.iterator]();
do {
  const result = iterator.next();
  if (result.done) break;
  const item = result.value;

  console.log(item);
} while (true);

barring a few missing try/catch wrappers. You can see in the spec here that it calls .next with no arguments:

Let nextResult be ? Call(iteratorRecord.[[NextMethod]], iteratorRecord.[[Iterator]], « »).

e.g.

iterator.next.apply(iterator, []);

calling next() with an empty array of arguments.