Please note, this is a STATIC archive of website developer.mozilla.org from 03 Nov 2016, cach3.com does not collect or store any user information, there is no "phishing" involved.

Revision 985311 of Iterators and generators

  • Revision slug: Web/JavaScript/Guide/Iterators_and_Generators
  • Revision title: Iterators and generators
  • Revision id: 985311
  • Created:
  • Creator: fscholz
  • Is current revision? No
  • Comment bug 1226545 - use modern fibonacci with 0. Doesn't matter or change much for the example, though.

Revision Content

{{jsSidebar("JavaScript Guide")}} {{PreviousNext("Web/JavaScript/Guide/Details_of_the_Object_Model", "Web/JavaScript/Guide/Meta_programming")}}

Processing each of the items in a collection is a very common operation. JavaScript provides a number of ways of iterating over a collection, from simple {{jsxref("Statements/for","for")}} loops to {{jsxref("Global_Objects/Array/map","map()")}} and {{jsxref("Global_Objects/Array/filter","filter()")}}. Iterators and Generators bring the concept of iteration directly into the core language and provide a mechanism for customizing the behavior of {{jsxref("Statements/for...of","for...of")}} loops.

For details, see also:

  • Iteration protocols
  • {{jsxref("Statements/for...of","for...of")}}
  • {{jsxref("Statements/function*","function*")}} and {{jsxref("Generator")}}
  • {{jsxref("Operators/yield","yield")}} and {{jsxref("Operators/yield*","yield*")}}

Iterators

An object is an iterator when it knows how to access items from a collection one at a time, while keeping track of its current position within that sequence. In JavaScript an iterator is an object that provides a next() method which returns the next item in the sequence. This method returns an object with two properties: done and value.

Once created, an iterator object can be used explicitly by repeatedly calling next().

function makeIterator(array){
    var nextIndex = 0;
    
    return {
       next: function(){
           return nextIndex < array.length ?
               {value: array[nextIndex++], done: false} :
               {done: true};
       }
    }
}

Once initialized, the next() method can be called to access key-value pairs from the object in turn:

var it = makeIterator(['yo', 'ya']);
console.log(it.next().value); // 'yo'
console.log(it.next().value); // 'ya'
console.log(it.next().done);  // true

Iterables

An object is iterable if it defines its iteration behavior, such as what values are looped over in a {{jsxref("Statements/for...of", "for..of")}} construct. Some built-in types, such as {{jsxref("Array")}} or {{jsxref("Map")}}, have a default iteration behavior, while other types (such as {{jsxref("Object")}}) do not.

In order to be iterable, an object must implement the @@iterator method, meaning that the object (or one of the objects up its prototype chain) must have a property with a {{jsxref("Symbol.iterator")}} key:

User-defined iterables

We can make our own iterables like this:

var myIterable = {}
myIterable[Symbol.iterator] = function* () {
    yield 1;
    yield 2;
    yield 3;
};
[...myIterable] // [1, 2, 3]

Built-in iterables

{{jsxref("String")}}, {{jsxref("Array")}}, {{jsxref("TypedArray")}}, {{jsxref("Map")}} and {{jsxref("Set")}} are all built-in iterables, because the prototype objects of them all have a {{jsxref("Symbol.iterator")}} method.

Syntaxes expecting iterables

Some statements and expressions are expecting iterables, for example the {{jsxref("Statements/for...of","for-of")}} loops, {{jsxref("Operators/Spread_operator","spread operator","","true")}}, {{jsxref("Operators/yield*","yield*")}}, and {{jsxref("Operators/Destructuring_assignment","destructuring assignment","","true")}}.

for(let value of ["a", "b", "c"]){
    console.log(value)
}
// "a"
// "b"
// "c"

[..."abc"] // ["a", "b", "c"]

function* gen(){
  yield* ["a", "b", "c"]
}

gen().next() // { value:"a", done:false }

[a, b, c] = new Set(["a", "b", "c"])
a // "a"

Generators

While custom iterators are a useful tool, their creation requires careful programming due to the need to explicitly maintain their internal state. {{jsxref("Global_Objects/Generator","Generators","","true")}} provide a powerful alternative: they allow you to define an iterative algorithm by writing a single function which can maintain its own state.

A generator is a special type of function that works as a factory for iterators. A function becomes a generator if it contains one or more {{jsxref("Operators/yield","yield")}} expressions and if it uses the {{jsxref("Statements/function*","function*")}} syntax.

function* idMaker(){
  var index = 0;
  while(true)
    yield index++;
}

var gen = idMaker();

console.log(gen.next().value); // 0
console.log(gen.next().value); // 1
console.log(gen.next().value); // 2
// ...

Advanced generators

Generators compute their yielded values on demand, which allows them to efficiently represent sequences that are expensive to compute, or even infinite sequences as demonstrated above.

The {{jsxref("Global_Objects/Generator/next","next()")}} method also accepts a value which can be used to modify the internal state of the generator. A value passed to next() will be treated as the result of the last yield expression that paused the generator.

Here is the fibonacci generator using next(x) to restart the sequence:

function* fibonacci(){
  var fn1 = 0;
  var fn2 = 1;
  while (true){  
    var current = fn1;
    fn1 = fn2;
    fn2 = current + fn1;
    var reset = yield current;
    if (reset){
        fn1 = 0;
        fn2 = 1;
    }
  }
}

var sequence = fibonacci();
console.log(sequence.next().value);     // 0
console.log(sequence.next().value);     // 1
console.log(sequence.next().value);     // 1
console.log(sequence.next().value);     // 2
console.log(sequence.next().value);     // 3
console.log(sequence.next().value);     // 5
console.log(sequence.next().value);     // 8
console.log(sequence.next(true).value); // 0
console.log(sequence.next().value);     // 1
console.log(sequence.next().value);     // 1
console.log(sequence.next().value);     // 2
Note: As a point of interest, calling next(undefined) is equivalent to calling next(). However, starting a newborn generator with any value other than undefined when calling next() will result in a TypeError exception.

You can force a generator to throw an exception by calling its {{jsxref("Global_Objects/Generator/throw","throw()")}} method and passing the exception value it should throw. This exception will be thrown from the current suspended context of the generator, as if the yield that is currently suspended were instead a throw value statement.

If a yield is not encountered during the processing of the thrown exception, then the exception will propagate up through the call to throw(), and subsequent calls to next() will result in the done property being true.

Generators have a {{jsxref("Global_Objects/Generator/return","return(value)")}} method that returns the given value and finishes the generator itself.

{{PreviousNext("Web/JavaScript/Guide/Details_of_the_Object_Model", "Web/JavaScript/Guide/Meta_programming")}}

Revision Source

<div>{{jsSidebar("JavaScript Guide")}} {{PreviousNext("Web/JavaScript/Guide/Details_of_the_Object_Model", "Web/JavaScript/Guide/Meta_programming")}}</div>

<p class="summary">Processing each of the items in a collection is a very common operation. JavaScript provides a number of ways of iterating over a collection, from simple {{jsxref("Statements/for","for")}} loops to {{jsxref("Global_Objects/Array/map","map()")}} and {{jsxref("Global_Objects/Array/filter","filter()")}}. Iterators and Generators bring the concept of iteration directly into the core language and provide a mechanism for customizing the behavior of {{jsxref("Statements/for...of","for...of")}} loops.</p>

<p>For details, see also:</p>

<ul>
 <li><a href="/en-US/docs/Web/JavaScript/Reference/Iteration_protocols">Iteration protocols</a></li>
 <li>{{jsxref("Statements/for...of","for...of")}}</li>
 <li>{{jsxref("Statements/function*","function*")}} and {{jsxref("Generator")}}</li>
 <li>{{jsxref("Operators/yield","yield")}} and {{jsxref("Operators/yield*","yield*")}}</li>
</ul>

<h2 id="Iterators">Iterators</h2>

<p>An object is an <strong>iterator</strong> when it knows how to access items from a collection one at a time, while keeping track of its current position within that sequence. In JavaScript an iterator is an object that provides a <code>next()</code> method which returns the next item in the sequence. This method returns an object with two properties: <code>done</code> and <code>value</code>.</p>

<p>Once created, an iterator object can be used explicitly by repeatedly calling <code>next()</code>.</p>

<pre class="brush: js">
function makeIterator(array){
    var nextIndex = 0;
    
    return {
       next: function(){
           return nextIndex &lt; array.length ?
               {value: array[nextIndex++], done: false} :
               {done: true};
       }
    }
}</pre>

<p>Once initialized, the <code>next()</code> method can be called to access key-value pairs from the object in turn:</p>

<pre class="brush: js">
var it = makeIterator(['yo', 'ya']);
console.log(it.next().value); // 'yo'
console.log(it.next().value); // 'ya'
console.log(it.next().done);  // true</pre>

<h2 id="Iterables">Iterables</h2>

<p>An object is <strong>iterable</strong> if it defines its iteration behavior, such as what values are looped over in a {{jsxref("Statements/for...of", "for..of")}} construct. Some built-in types, such as {{jsxref("Array")}} or {{jsxref("Map")}}, have a default iteration behavior, while other types (such as {{jsxref("Object")}}) do not.</p>

<p>In order to be <strong>iterable</strong>, an object must implement the <strong>@@iterator</strong> method, meaning that the object (or one of the objects up its <a href="/en-US/docs/Web/JavaScript/Guide/Inheritance_and_the_prototype_chain">prototype chain</a>) must have a property with a {{jsxref("Symbol.iterator")}} key:</p>

<h3 id="User-defined_iterables">User-defined iterables</h3>

<p>We can make our own iterables like this:</p>

<pre class="brush: js">
var myIterable = {}
myIterable[Symbol.iterator] = function* () {
&nbsp;&nbsp;&nbsp; yield 1;
&nbsp;&nbsp;&nbsp; yield 2;
&nbsp;&nbsp;&nbsp; yield 3;
};
[...myIterable] // [1, 2, 3]
</pre>

<h3 id="Built-in_iterables">Built-in iterables</h3>

<p>{{jsxref("String")}}, {{jsxref("Array")}}, {{jsxref("TypedArray")}}, {{jsxref("Map")}} and {{jsxref("Set")}} are all built-in iterables, because the prototype objects of them all have a {{jsxref("Symbol.iterator")}} method.</p>

<h3 id="Syntaxes_expecting_iterables">Syntaxes expecting iterables</h3>

<p>Some statements and expressions are expecting iterables, for example the {{jsxref("Statements/for...of","for-of")}} loops, {{jsxref("Operators/Spread_operator","spread operator","","true")}}, {{jsxref("Operators/yield*","yield*")}}, and {{jsxref("Operators/Destructuring_assignment","destructuring assignment","","true")}}.</p>

<pre class="brush: js">
for(let value of ["a", "b", "c"]){
    console.log(value)
}
// "a"
// "b"
// "c"

[..."abc"] // ["a", "b", "c"]

function* gen(){
  yield* ["a", "b", "c"]
}

gen().next() // { value:"a", done:false }

[a, b, c] = new Set(["a", "b", "c"])
a // "a"

</pre>

<h2 id="Generators">Generators</h2>

<p>While custom iterators are a useful tool, their creation requires careful programming due to the need to explicitly maintain their internal state. <strong>{{jsxref("Global_Objects/Generator","Generators","","true")}}</strong> provide a powerful alternative: they allow you to define an iterative algorithm by writing a single function which can maintain its own state.</p>

<p>A generator is a special type of function that works as a factory for iterators. A function becomes a generator if it contains one or more {{jsxref("Operators/yield","yield")}} expressions and if it uses the {{jsxref("Statements/function*","function*")}} syntax.</p>

<pre class="brush: js">
function* idMaker(){
  var index = 0;
  while(true)
    yield index++;
}

var gen = idMaker();

console.log(gen.next().value); // 0
console.log(gen.next().value); // 1
console.log(gen.next().value); // 2
// ...</pre>

<h2 id="Advanced_generators">Advanced generators</h2>

<p>Generators compute their yielded values on demand, which allows them to efficiently represent sequences that are expensive to compute, or even infinite sequences as demonstrated above.</p>

<p>The {{jsxref("Global_Objects/Generator/next","next()")}} method also accepts a value which can be used to modify the internal state of the generator. A value passed to <code>next()</code> will be treated as the result of the last <code>yield</code> expression that paused the generator.</p>

<p>Here is the fibonacci generator using <code>next(x)</code> to restart the sequence:</p>

<pre class="brush: js">
function* fibonacci(){
  var fn1 = 0;
  var fn2 = 1;
  while (true){  
    var current = fn1;
    fn1 = fn2;
    fn2 = current + fn1;
    var reset = yield current;
    if (reset){
        fn1 = 0;
        fn2 = 1;
    }
  }
}

var sequence = fibonacci();
console.log(sequence.next().value);     // 0
console.log(sequence.next().value);     // 1
console.log(sequence.next().value);     // 1
console.log(sequence.next().value);     // 2
console.log(sequence.next().value);     // 3
console.log(sequence.next().value);     // 5
console.log(sequence.next().value);     // 8
console.log(sequence.next(true).value); // 0
console.log(sequence.next().value);     // 1
console.log(sequence.next().value);     // 1
console.log(sequence.next().value);     // 2</pre>

<div class="note"><strong>Note:</strong> As a point of interest, calling <code>next(undefined)</code> is equivalent to calling <code>next()</code>. However, starting a newborn generator with any value other than undefined when calling <code>next()</code> will result in a <code>TypeError</code> exception.</div>

<p>You can force a generator to throw an exception by calling its {{jsxref("Global_Objects/Generator/throw","throw()")}} method and passing the exception value it should throw. This exception will be thrown from the current suspended context of the generator, as if the <code>yield</code> that is currently suspended were instead a <code>throw <em>value</em></code> statement.</p>

<p>If a <code>yield</code> is not encountered during the processing of the thrown exception, then the exception will propagate up through the call to <code>throw()</code>, and subsequent calls to <code>next()</code> will result in the <code>done</code> property being <code>true</code>.</p>

<p>Generators have a {{jsxref("Global_Objects/Generator/return","return(value)")}} method that returns the given value and finishes the generator itself.</p>

<p>{{PreviousNext("Web/JavaScript/Guide/Details_of_the_Object_Model", "Web/JavaScript/Guide/Meta_programming")}}</p>
Revert to this revision