v153 · javascript · iterator helpers

Iterator.zip and Iterator.zipKeyed

Walking two sequences together is a loop everybody has written and nobody has written the same way twice. What happens when one runs out? Does the other get closed? Joint Iteration answers both, explicitly, with a mode you choose instead of a bug you discover.

concepts

  1. The three modes, and the padding

    Give three lists of different lengths to shortest, longest and strict and watch them disagree — one truncates, one pads with what you specify, one refuses. The choice is the API's whole point.

  2. Records from columns

    zipKeyed takes an object of iterables and yields objects. Paste column-oriented data — the shape every CSV parser and every analytics export hands you — and get records out, with the ragged-column case handled rather than hidden.

  3. Laziness, and who gets closed

    Zip pulls one value from each source per step and never runs ahead. When it stops, it must close the sources it is abandoning. Both behaviours are observable here — with counters and close hooks on real generators, not a description of what should happen.

why it shipped

Every codebase has a hand-rolled zip, and they differ in the cases that matter. Indexing two arrays by i silently produces undefined past the end of the shorter one. A while loop over two iterators usually forgets to call return() on the one it abandons, so a generator's finally block never runs and whatever it was holding stays held.

The proposal makes the choice explicit — truncate, pad, or throw — and specifies the cleanup, so the abandoned iterators are closed whichever way the walk ends, including when your own mapping function throws mid-step.

the API

// Positional: an array of iterables in, an iterator of arrays out.
Iterator.zip([[1, 2, 3], "abc"]);                   // [1,"a"], [2,"b"], [3,"c"]

// The mode decides what happens when the lengths differ.
Iterator.zip([[1, 2, 3], [10]], { mode: "shortest" });  // one step
Iterator.zip([[1, 2, 3], [10]], { mode: "longest", padding: [0, 0] });
Iterator.zip([[1, 2, 3], [10]], { mode: "strict" });    // throws on step 2

// Keyed: an object of iterables in, an iterator of objects out.
Iterator.zipKeyed({ id: ids, name: names });        // { id, name }, …

enabling it now

Chrome ships both methods behind V8's staging flag before they reach stable. Verified on Chrome 150 on this machine: with the flag off, Iterator.prototype.join, Iterator.zip and Iterator.zipKeyed are all undefined; with it on, all three are functions and every demo here takes the native path.

google-chrome --js-flags=--harmony        # or --js-flags=--js-staging

There is no chrome://flags entry for this one — staged JavaScript features are enabled by passing the V8 flag on the command line. chrome://flags/#enable-experimental-web-platform-features does not enable them; that switch covers Blink, not V8, and was measured making no difference here.

references