"for of" iteration support.
example:
function seq(n) {
var i = 0;
return {
[Symbol.iterator]() {
return {
next() {
if (i == n) {
return { done: true };
}
return { value: i++ };
}
};
}
}
}
for (var x of seq(10)) {
console.log(x);
}
Here's some PoC: https://gist.github.com/VBart/ce0bad1f15a337eff49f1336311d8381
% build/njs -c "var a = ['a', 'b', 'c']; for (var e of a) console.log(e);"
a
b
c
@VBart
Hi!
Actually, there is a huge difference between for of and for in:
function collatz(n) {
var seq = [n];
for (var x of seq) {
if (x == 1) {
return seq;
}
seq.push(x & 1 ? 3 * x + 1 : x >> 1);
}
}
console.log(collatz(42));
@xeioex
Please take a look. Introduced initial iterator support. test262.diff
Practically useless, because iteration will generate O(n) temporary objects. But by introducing some new vmcodes, the O(1) for( of ) can be made.
$ cat collatz.js
function collatz2(n) {
var seq = [n];
var it = seq.values();
for (var x = it.next(); !x.done; x = it.next()) {
if (x.value == 1) {
return seq;
}
seq.push(x.value & 1 ? 3 * x.value + 1 : x.value >> 1);
}
}
console.log(collatz2(42));
$ build/njs collatz.js
[42,21,64,32,16,8,4,2,1]
@drsm
But by introducing some new vmcodes, the O(1) for( of ) can be made.
you are suggesting to hide the returned value right and operate on njs_value_iterator_t?
for (var x of iterable) {
}MAKE ITER # it = iterable[Symbol.iterator]()
again: ITER NEXT # it.next()
BODY # ...
ITER CHECK IF DONE # !it.done
GOTO again
So, making the real object, may be avoided.
you are suggesting to hide the returned value right and operate on njs_value_iterator_t? So, making the real object may be avoided.
Yes. To avoid useless https://tc39.es/ecma262/#sec-createiterresultobject when it is possible.
I plan to do something like this:
# for ($x of $o)
$o # iterable
$i # iterator object
$x # value
ITERATOR # iterator($i $o)
GOTO check
again: BODY # ...
check: ITERATE # iterate($x $i)
GOTO again IF $x is valid
function iterator($i, $o) {
// https://tc39.es/ecma262/#sec-getiterator
$i = $o[Symbol.iterator]()
}
function iterate($x, $i) {
if (is_internal_iterator($i)) {
internal_iterate($x, $i); // ArrayIterator, StringIterator, SetIterator, MapIterator ...
return;
}
// slow path
// wrap https://tc39.es/ecma262/#sec-iteratorstep
var o = $i.next();
if (o.done) {
set_invalid($x);
} else {
set($x, o.value);
}
}
But abusing NJS_VALUE_INVALID as the end of the sequence will not work:
var a = [1,2,3];
var x;
for (x of a) {}
console.log(x); // x should point to the last valid value
3
Also, we can't operate directly on the iterator state:
var a = [1,2,3,4,5,6];
var it = a.values();
for (var x of it) { it.next(); console.log(x); }
1
3
5
BTW, I'm just trying to figure out how the VM works. So, i'm sure there is a better way :)
Patch updated, added String.prototype[Symbol.iterator]
@drsm
Introduced initial iterator support.
Thanks, committed.
This is the feature is miss the most now because its proper transpilation brings a significant overhead. Most of the time I iterate just over Arrays, so it can be transpiled into a simple and efficient for loop over indexes, but Babel transpiler cannot know that. There’s an option assumeArray to always transpile for-of into basic for loop, but it’s risky because the code will fail in runtime if the value is not an array but a real iterable.