Coming back to JavaScript after focusing on Python, I was surprised that using break when iterating over a generator closes the generator. This post has an example of that behaviour and a comparison with Python’s behaviour.

Python and JavaScript both have the concept of generators, functions which can yield multiple values, pausing after each yield until the consumer asks for the next value.

# Python
def my_generator():
	yield 1
	yield 2
	yield 3

iterator = my_generator()

print(iterator.next())
print("something else")
for item in iterator:
  print(item)

# Outputs:
# 1
# something else
# 2
# 3
// Javascript
function* myGenerator() {
  yield 1;
  yield 2;
  yield 3;
}

const iterator = myGenerator();

console.log(iterator.next().value);
console.log("something else");
for (const item of iterator) {
  console.log(item);
}

// Outputs:
// 1
// something else
// 2
// 3

These two things look basically identical. The iterator object we get back in the two cases has pretty much the same three main functions:

  • __next__(Python)/ next(JavaScript)
    • Run the generator up to the next yield and get the yielded value
  • throw(exception)
    • Force the generator to throw the given exception from its current suspended position
  • close(Python)/return(value)(JavaScript)
    • Close the generator, making sure any finally blocks are run. Can’t get values out of this generator after this (unless there are yields in the finally for some reason).

The interesting difference that surprised me is that in JavaScript, calling break whilst iterating over a generator calls return on the generator, so you can’t really keep using that generator:

# Python
def my_generator():
  try:
    yield 1
    yield 2
  finally:
    print("finally")

it = my_generator()

# Print one item then break
for item in it:
  print(item)
  break

# Print the rest of the items
for item in it:
  print(item)

# Outputs:
# 1
# 2
# finally
// Javascript
function* myGenerator() {
  try {
    yield 1;
    yield 2;
  } finally {
    console.log("finally");
  }
}

const it = myGenerator();

// Print one item then break
for (const item of it) {
  console.log(item);
  break;
}

// Try to print the rest of the items
// (doesn't work)
for (const item of it) {
  console.log(item);
}

// Outputs:
// 1
// finally

In Python, the close gets called in the __del__ of the iterator object. That is, it gets closed when it gets garbage collected. This isn’t really possible in JavaScript due to the lack of any standardised garbage collection scheme, so I guess they had to do something else to make sure ‘finally’ is more likely to actually be called. That said, if it’s iterated outside of a for..of, it’s easy enough to just not close it and the finally will never run.

// Javascript
function* myGenerator() {
  try {
    yield 1;
    yield 2;
  } finally {
    console.log("finally");
  }
}

const iterator = myGenerator();
console.log(iterator.next().value);

// Outputs:
// 1
// (never outputs 'finally')

Further Reading