Iterators

What is an iterator?

  • An iterator is an object that helps us traverse some data structure. An iterator is an object.
  • This object needs to conformIterator Protocol.
    • The iterator protocol defines a standard way to produce a set of values, whether finite or wireless.
    • In JS this standard is a specific Next method.
  • The next method has the following requirements:
    • A function with no arguments or one argument returns an object that should have the following two properties:
    • Done (Boolean type):
      • False if the iterator can produce the next value in the sequence (equivalent to not specifying done).
      • True if the iterator has iterated through the sequence. In this case, value is optional; if value still exists, it is the default value returned at the end of the iteration.
    • Value: Any JavaScript code returned by the iterator, which can be omitted if done is true.

Iterator code

function createIterator(arr) {
    const length = arr.length
    let index = 0
    
    return {
        next() {
            return { done: index >= length, value: arr[index++] }
        }
    }
}
const arr = ['aaa'.'bbb'.'ccc']

// The resulting iterator is an iterator because createIterator returns an object that satisfies the iterator protocol.
const iterator = createIterator(arr)
console.log(iterator.next());   // { done: false, value: 'aaa' }
console.log(iterator.next());   // { done: false, value: 'bbb' }
console.log(iterator.next());   // { done: false, value: 'ccc' }
console.log(iterator.next());   // { done: true, value: undefined }
Copy the code

What is theiterable?

  • An object is an iterable when it implements the Iterable Protocol.
  • This object requires that the @@iterator method be implemented and that the Symbol. Iterator is used to access the property in the code.
  • String, Array, Map, Set, Arguments, NodeList collections, etc are all iterable. An iterator object can be generated by calling symbol. iterator.

What are the benefits of iterables?

When an object becomes iterable and performs some iteration operations, such as for… The **@@iterator method ** is used when the of operation is performed.

Application of iterable

  • JavaScript syntax: for.. Of, spread syntax, yield, deconstruct assignment.
  • When creating objects: New Set(iterable), New Map(iterable), New WeakMap(iterable), New WeakSet(iterable), etc.
  • Some method calls: promise.all (itrable), promise.race (iterable), array.from (iterable), etc.

Iterable code

const obj = {
    val: ['aaa'.'bbb'.'ccc'],
    [Symbol.iterator]() {
        let index = 0
        return {
        // Next uses the arrow function so that this can point to obj.
            next: () = > {
                return { 
                    done: this.val.length <= index,
                    value: this.val[index++]
                }
            }
        }
    }
}

for (const i of obj) {
    console.log(i) // The print results are aaa, BBB, CCC
}
Copy the code

Iterator interrupt

  • Iterators in some cases will run inInterrupt without complete iteration:
    • For example, loop operations that are interrupted by a break, continue, return, or throw during traversal.
    • For example, when you deconstruct, you don’t deconstruct all the values.
  • If you want to listen for interrupts, you can add a return method. The return method returns an object, or an error if it does not return an object.
const obj = {
    val: ['aaa'.'bbb'.'ccc'],
    [Symbol.iterator]() {
        let index = 0
        return {
            next: () = > {
                return {
                    done: this.val.length <= index,
                    value: this.val[index++]
                }
            },
            return: () = > {
                // This function is called when the iterator breaks
                // Must return an object
                return{}}}}}Copy the code

The generator

What is a generator?

  • Generators are a new function control scheme for ES6. It gives us more flexibility to control when a function continues execution, pauses execution, etc.
  • A generator function is also a function, but there are some differences with ordinary functions:
    • Generator functions need a symbol * at the end of function
    • Generator functions can control the flow of their execution through the yield keyword.
    • The return value of the Generator function is a Generator. A generator is actually a special kind of iterator.

Generator function code

function* foo() {   // The function definition is *
    console.log(1)
    yield 1   // The value after yield is used as the value of the returned iterator object
    console.log(2)
    yield 2
    console.log(3)}const generator = foo()  // Generator is a generator
// Execute to the first yield and pause
console.log(generator.next()) // 1 { value: 1, done: false }
// Execute to the second yield and pause
console.log(generator.next()) // 2 { value: 2, done: false }
// Execute the rest of the code
console.log(generator.next()) // 3 { value: undefined, done: true }
Copy the code
  • When function foo is executed, you find that the function body is not executed at all; it just returns a generator object.
  • If you want to execute the code inside the function body, call next.
  • If you do not want next to return an iterator whose value is undefined, yield can be used to return the result.

Generator functions pass arguments

  • Since the generator can pause to fragment execution, we can pass parameters to each fragment.
  • When we call the next function, we can pass an argument to the next function, which will be the return value of the previous yield statement.
  • Normally, no arguments are passed to the first next because there is no yield in front of the first next
function* bar() {
    const num1 = yield 'a'
    const num2 = yield num1
    const num3 = yield num2
}

const generator = bar()
console.log(generator.next()) // { value: 'a', done: false }
console.log(generator.next('b')) // { value: 'b', done: false }
console.log(generator.next('c')) // { value: 'c', done: false }
console.log(generator.next()) // { value: undefined, done: true }
Copy the code

End the generator function early -return function

  • A return function can also pass arguments to a generator function. However, after the return function, the generator function ends, and subsequent calls to Next do not continue generating values. When return is called, value is the argument passed in for return. ,
function* bax() {
    const val1 = yield 'a'
    console.log('val1', val1) // It will not be executed because the return function is called
}
const generator = bax()
console.log(generator.next()) // { value: 'a', done: false }
console.log(generator.return(1)) // { value: 1, done: true }
console.log(generator.next()) // { value: undefined, done: true }
Copy the code

The generator throws an exception-throw function

  • In addition to passing arguments inside a generator function, you can also throw an exception inside the generator line.
  • After an exception is thrown, it can be caught in a generator function.
function* baz() {
    console.log('start')
    
    try {
        yield 1
    } catch (err) {
        console.log('Catch exception inside generator function', err) // This will catch 'err message'
        yield 'Catch uses yield inside' 
    }
    yield 'Catch external use yield'
}

const generator = baz()
console.log(generator.next()) // { value: 1, done: false }
console.log(generator.throw('err message'))  // {value: 'catch ', done: false}
console.log(generator.next()) // {value: 'catch ', done: false}

Copy the code

Generators replace iterators

  • A generator is a special type of iterator, and in some cases you can use a generator instead of an iterator.
// Implement an object that can use for... Of traversal
function* generator() {
    const keys = Object.keys(this)
    
    for (let i = 0; i < keys.length; i++) {
        yield this[keys[i]]
    }
}

const obj = {
    a: 1.b: 2.c: 3.d: 4[Symbol.iterator]: generator
}

for (const item of obj) {
    console.log(item)  // 1, 2, 3, 4
}
Copy the code