Use for...of with a custom iterable.

for-of-iterable `for...of` reads from any object that provides `Symbol.iterator`. The loop keeps asking for the next value until the iterator is done.

For Of Iterables

stop
for_of_iterable.js
Replay: real traced execution (multi-file project)
const stop = 3;
const values = [];

const range = {
  [Symbol.iterator]() {
    let current = 1;
    return {
      next() {
        if (current <= stop) {
          const value = current;
          current = current + 1;
          return { value, done: false };
        }
        return { value: undefined, done: true };
      },
    };
  },
};

for (const value of range) {
  values.push(value * 10);
}

console.log("stop=" + stop);
console.log("scaled=" + values.join("-"));
const stop = 2;
const values = [];

const range = {
  [Symbol.iterator]() {
    let current = 1;
    return {
      next() {
        if (current <= stop) {
          const value = current;
          current = current + 1;
          return { value, done: false };
        }
        return { value: undefined, done: true };
      },
    };
  },
};

for (const value of range) {
  values.push(value * 10);
}

console.log("stop=" + stop);
console.log("scaled=" + values.join("-"));
const stop = 4;
const values = [];

const range = {
  [Symbol.iterator]() {
    let current = 1;
    return {
      next() {
        if (current <= stop) {
          const value = current;
          current = current + 1;
          return { value, done: false };
        }
        return { value: undefined, done: true };
      },
    };
  },
};

for (const value of range) {
  values.push(value * 10);
}

console.log("stop=" + stop);
console.log("scaled=" + values.join("-"));
  1. stop ← 3

    1const stop→ 3 = 3; //@stop=2, 42const values = [];
  2. values ← (empty)

    1const stop = 3; //@stop=2, 42const values→ (empty) = [];
  3. range ← [object Object]

    1const stop = 3; //@stop=2, 42const values = [];34const range→ [object Object] = {5  [Symbol.iterator]() {6    let current = 1;7    return {8      next() {9        if (current <= stop) {10          const value = current;11          current = current + 1;12          return { value, done: false };13        }14        return { value: undefined, done: true };15      },16    };17  },18};
  4. values ← 10, value ← 1

    20for (const value of range) {21  values.push(value→ 1 * 10);22}
    values this step(empty) 10values
  5. values ← 10,20, value ← 2

    20for (const value of range) {21  values.push(value→ 2 * 10);22}
    values this step10 10,20values
  6. values ← 10,20,30, value ← 3

    20for (const value of range) {21  values.push(value→ 3 * 10);22}
    values this step10,20 10,20,30values
  7. console.log("stop=" + stop);

    21  values.push(value * 10);22}2324console.log("stop=" + stop3);25console.log("scaled=" + values.join("-"));
    outputstop=3
  8. console.log("scaled=" + values.join("-"));

    24console.log("stop=" + stop);25console.log("scaled=" + values10,20,30.join("-"));
    outputscaled=10-20-30
  1. stop ← 2

    1const stop→ 2 = 2;2const values = [];
  2. values ← (empty)

    1const stop = 2;2const values→ (empty) = [];
  3. range ← [object Object]

    1const stop = 2;2const values = [];34const range→ [object Object] = {5  [Symbol.iterator]() {6    let current = 1;7    return {8      next() {9        if (current <= stop) {10          const value = current;11          current = current + 1;12          return { value, done: false };13        }14        return { value: undefined, done: true };15      },16    };17  },18};
  4. values ← 10, value ← 1

    20for (const value of range) {21  values.push(value→ 1 * 10);22}
    values this step(empty) 10values
  5. values ← 10,20, value ← 2

    20for (const value of range) {21  values.push(value→ 2 * 10);22}
    values this step10 10,20values
  6. console.log("stop=" + stop);

    21  values.push(value * 10);22}2324console.log("stop=" + stop2);25console.log("scaled=" + values.join("-"));
    outputstop=2
  7. console.log("scaled=" + values.join("-"));

    24console.log("stop=" + stop);25console.log("scaled=" + values10,20.join("-"));
    outputscaled=10-20
  1. stop ← 4

    1const stop→ 4 = 4;2const values = [];
  2. values ← (empty)

    1const stop = 4;2const values→ (empty) = [];
  3. range ← [object Object]

    1const stop = 4;2const values = [];34const range→ [object Object] = {5  [Symbol.iterator]() {6    let current = 1;7    return {8      next() {9        if (current <= stop) {10          const value = current;11          current = current + 1;12          return { value, done: false };13        }14        return { value: undefined, done: true };15      },16    };17  },18};
  4. values ← 10, value ← 1

    20for (const value of range) {21  values.push(value→ 1 * 10);22}
    values this step(empty) 10values
  5. values ← 10,20, value ← 2

    20for (const value of range) {21  values.push(value→ 2 * 10);22}
    values this step10 10,20values
  6. values ← 10,20,30, value ← 3

    20for (const value of range) {21  values.push(value→ 3 * 10);22}
    values this step10,20 10,20,30values
  7. values ← 10,20,30,40, value ← 4

    20for (const value of range) {21  values.push(value→ 4 * 10);22}
    values this step10,20,30 10,20,30,40values
  8. console.log("stop=" + stop);

    21  values.push(value * 10);22}2324console.log("stop=" + stop4);25console.log("scaled=" + values.join("-"));
    outputstop=4
  9. console.log("scaled=" + values.join("-"));

    24console.log("stop=" + stop);25console.log("scaled=" + values10,20,30,40.join("-"));
    outputscaled=10-20-30-40