Forwarding `return()` in generators

Ron Buckton Ron.Buckton at microsoft.com
Tue Mar 24 22:35:51 UTC 2015


Is your goal to wrap a generator, as it seems you are propagating the exception of the caller by calling iterator.throw(). However, you do not seem to be propagating the sent value, so the protocol here isn’t fully implmeneted.

If you just want to iterate values (and don’t really care about the return value of the iterable or propagating a thrown exception, you could write:

```js
function* take(n, iterable) {
  n |= 0;
  if (n <= 0) {
    return;
  }
  // let for..of call return()
  for (let value of iterable) {
    yield value;
    if (n-- <= 0) {
      return;
    }
  }
}
```

If you want to support the full communication channel of a generator, you could write:

```js
function* take(n, iterable) {
  let iterator = iterable[Symbol.iterator]();
  let step = () => iterator.next();
  n |= 0;
  // try..finally outside of loop
  try {
    let sent;
    while (n > 0) {
      let { value, done } = step();
      if (done) {
        return value;
      }
      n--;
      // try..catch local to the yield
      try {
        sent = yield value;
        step = () => iterator.next(sent);
      }
      catch (e) {
        if (typeof iterator.throw === "function") {
          step = () => iterator.throw(e);
        }
        else {
          throw e;
        }
      }
    }
  }
  finally {
    if (typeof iterator.return === "function") {
      iterator.return();
    }
  }
}
```

From: es-discuss [mailto:es-discuss-bounces at mozilla.org] On Behalf Of Axel Rauschmayer
Sent: Tuesday, March 24, 2015 2:28 PM
To: Bergi
Cc: es-discuss list
Subject: Re: Forwarding `return()` in generators

Right, it doesn’t look like one needs to know the returned value when forwarding `return()`.

But: you need to guard against other ways of reaching `finally`. Maybe like this:

```js
function* take(n, iterable) {
    let iterator = iterable[Symbol.iterator]();
    n = +n; // make sure it's a number, so that n>0 does never throw
    let forwardReturn = true;
    try {
        while (n > 0) {
            let item = iterator.next();
            if (item.done) {
                forwardReturn = false;
                return item.value;
            }
            yield item.value;
            n--;
        }
        forwardReturn = false;
    } catch (e) {
        forwardReturn = false;
        iterator.throw(e);
    } finally {
        if (forwardReturn) {
            iterator.return();
        }
    }
}
```
The above code also has the additional nice property that it call `.return()` on the iterator when `n` values have been taken out of it.

That’s not what all the other constructs in ES6 do: they only call `return()` if iteration stops abruptly.

Also missing from this code: checking whether the iterator actually has the methods `return()` and `throw()` and responding accordingly.

--
Dr. Axel Rauschmayer
axel at rauschma.de<mailto:axel at rauschma.de>
rauschma.de


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20150324/512068ef/attachment-0001.html>


More information about the es-discuss mailing list