Redux Middleware is used for React learning, and redux-Saga Middleware has a lot of iterators and generators to review, so it should be a separate article

Now that I’m attracted to your little sister, why don’t you take a look? ! 🤫 ha ha, nonsense not much to say, into the body hot ~

Iterators and iterable protocols

Iteration: fetching the next data in turn for processing (without relying on the collection data structure) in a certain logic; It is similar to traversal (traversal is the process of retrieving and processing data from a set of data structures (arrays or arrays like maps, sets, arrays, etc.)

Iterators: In our language, the JavaScript language dictates that if an object has a next method, and the next method is called, it returns a value that has at least value, Done is true, undefined) and done (Boolean iterates), then we can call this an iterator

// For example, the simplest result:
// Generate random numbers as an example:
const iterator = {
    next() {
        return {
            value: Math.rondom(),
            done: false}}}// An iterator that iterates three times:
const iteratorObj = {
    total: 3.idx: 0.next() {
        const res = {
            value: this.idx > this.total ? undefined : this.idx,
            done: this.idx > this.total
        }
        this.idx++
        return res
    }
}
Copy the code

For example, in point 4 of Summary of Interview Algorithm, I wrote about the Fibonacci sequence realization and some optimization schemes of other schemes. The Fibonacci sequence realization scheme using iterator is as follows (then, of course, it is a positive value and the result is irreversible. Unless otherwise controlled) :

const fibo = {
    a: 1.b: 1.curIdx: 1.next() {
        if (this.curIdx === 1 || this.curIdx === 2) {
            this.curIdx ++
            return {
                value: 1.done: false}}const c = this.a + this.b
        this.curIdx++
        [this.a, this.b] = [this.b, c]
        return {
            value: c,
            done: false}}}Copy the code

We can retrieve the data in turn by calling the Next method and determine if the iteration is over based on the done attribute of the returned object

Iterator Creator: A function that returns an iterator, or simply an iterator function

Iterable protocol

ES6 added for… The of loop is used to iterate over an object, so for… The of loop requires that the object be iterable (the object must satisfy the iterable protocol)

Iterable protocol: An object must have the well-known symbolic attribute symbol. iterator, which must be an iterator creation function with no arguments

for… Iterator (); iterator (); iterator (); iterator (); iterator (); iterator (); iterator () Internal code of of:

/ / such as:
for(const item of obj) {
    console.log(item) // Iterate over and print each item
}

//
const iterator = obj[Symbol.iterator]() // Get the iterator
let result = iterator.next()
while(! result.done) {const item = result.value
    
    console.log(item) // The code we wrote to print each item
    
    result = iterator.next()
}
Copy the code

The generator

generator

An object created by the constructor Generator that is both an iterator and an iterable (conforming to the iterable protocol above)

Pseudo code

const generator = new Generator()
generator.next() // Have the next method
generator[Symbol.iterator] // Function is iterable

for(const item of generator) {
    // Iterable, which can be used for... Of circulation
}
Copy the code

GeneratorFunctions are constructors used internally by the JS engine and are not provided to developers

generator function

Generator function (generator creator function), used to create a generator. Declare functions syntactically as function*

function* createGenerator() {
    // other code...
}
/ / or
function *createGenerator() {
    // other code...
}

const generator = createGenerator() // Get a generator

/ / so:
generator.next // => native code Function
generator[Symbol.iterator] // => native code Function

generator.next === generator[Symbol.iterator]().next // true
Copy the code

Characteristics of generator functions

  1. A generator function call that does not execute the function body inside the function body, but returns a generator (because execution of the function body inside the generator function is controlled by the returned generator).

  2. Each time the next method of the returned generator is called, the body of the generator function runs from the last yield statement (or the beginning of the function body) to the next yield statement (or the end of the function).

    The yield keyword can only be used in generator functions to pause the execution of the code inside the function and return data for the current iteration; The done of the object returned by the next method is set to true if there is no next yield

  3. The result of the expression after the yield keyword returns the value of the object as the next method

  4. Return “Any data…” {value: undefined, done: true}} {value: undefined, done: true}}

  5. When a generator calls the next method, it can pass an argument as the yield expression in the body of the generator function (the first time a generator calls the next method to pass an argument is meaningless and is ignored), as follows:

    function* createGenerator() {
        console.log('function start... ')
        let res = yield 1
        // The first iteration 
            
              is stuck in yield and assignment is not completed
            ()>
        // In the second iteration, the value of the newly passed argument is assigned to the res variable (undefined if it is not passed)
        console.log('logger - 1', res)
        res = yield 2
        console.log('logger - 2', res)
        res = yield 3
        console.log('logger - 3', res)
        return {
            desc: 'function end... '}}const generator = createGenerator() // Get the generator
    generator.next(111)
    / * print: 'function start... 'Returns: {value: 1, done: false} */
    
    generator.next(222)
    /* print: 'logger-1' 222 returns: {value: 2, done: false} */
    
    generator.next()
    /* print: 'logger-2' undefined returns: {value: 3, done: false} */
    
    generator.next(444)
    /* print: 'logger-3' 444 returns: {value: {desc: 'function end... ' }, done: true } */
    Copy the code

    Therefore, in the process of iteration, the next iteration needs the results returned by the previous iteration, which can be handled as follows:

    // Take the createGenerator function above as an example
    const generator = createGenerator() // Get the generator
    let result = generator.next() // The first call will return a value
    while(! result.done) {// The result of the last iteration is passed to the next iteration
        result = generator.next(result.value)
    }
    Copy the code

    Before ES7 async and await appear, we need pro.then() =>.then =>. Then to perform a series of asynchronous operations, so we can also use generators to complete each step:

    // Simulate a data request
    function getData() {
        return new Promise(resolve= > {
            setTimeout(() = > {
                resolve({
                    name: 'suressk'.age: 25.province: 'Hubei'})},2000)})}function* task() {
        console.log('get data... ')
        const data = yield getData() // value => Promise
        console.log('got data: ', data)
    }
    
    const generator = task()
    const { value: pro } = generator.next()
    // print: 'get data... '
    
    // 2 seconds later
    pro.then(data= > generator.next(data))
    // print: 'got data: ' { name: 'suressk', ... }
    Copy the code
    // Encapsulates a generator task's runtime function:
    function run(generatorFunc) {
        const generator = generatorFunc() // Get the generator
        next()
    
        /** * encapsulates the generator's next method * call for the next iteration */
        function next(nextVal) {
            const { value, done } = generator.next(nextVal)
            if (done) return // End of iteration
            if (isPromise(value)) {
                value.then(data= > next(data))
            } else {
                next(value)
            }
        }
    }
    
    // Obj is a Promise
    function isPromise(obj) {
        return!!!!! obj && (typeof obj === 'object' || typeof obj === 'function') &&
            typeof obj.then === 'function'
    }
    
    // The task generator function of the above code can be called directly
    run(task) // Asynchronous methods can also run if the task has multiple yield truncations inside it
    Copy the code
  6. The generator comes with a throw method that has the same effect as next, except that arguments passed by the next method are returned as normal values. The throw method passes an error object and sets the iterator state to the end of the iteration

    function* generatorFunc () {
        console.log('function start... ')
        let res = yield 1
        console.log('logger - 1', res)
        res = yield 2
        console.log('logger - 2', res)
        res = yield 3
        console.log('logger - 3', res)
        return 'function end... '
    }
    
    const generator = generatorFunc()
    generator.next() // The statement stops at yield 1
    /** * print: 'function start... ' * returns: { value: 1, done: false } */
    // If an error object is passed
    generator.next(new Error('Error reported ~')) // The statement stops at yield 2
    /** * print: 'logger-1' [error object (' error ~ ')] * returns: {value: 2, done: false} */
    generator.throw(new Error('Error reported ~')) // Throw an error and the iteration ends
    / * * * print: [error object (' error ~ ')] * returns: nothing... * /
    Next () ➡️ returns {value: undefined, done: true}
    Copy the code
  7. The generator takes a return method that terminates the generator function directly, and it can take an argument as the value property of the object that gets the return value from calling it (undefined if not passed).

    // Borrow the generator function generatorFunc above
    const generator = generatorFunc()
    
    generator.next() // The statement stops at yield 1
    generator.return() // End of iteration
    /**
    * returns: {value: undefined, done: true}
    */
    generator.return('abc')
    /**
    * returns: {value: 'abc', done: true}
    */
    // Continue calling the return method
    generator.return('The above iteration has been completed early.')
    /** * returns: {value: 'done: true} */
    Copy the code
  8. If you need to call another generator from within the generator, if you call it directly, you simply create a generator object where it was called; If the yield plus * call is used, it is executed step by step inside its generator

    function* g1() {
        console.log('g1 start... ')
        let res = yield 1
        console.log('g1 logger - 1', res)
        res = yield 2
        console.log('g1 logger - 2', res)
        res = yield 3
        console.log('g1 logger - 3', res)
        return 'g1 end... '
    }
    function* g2() {
        console.log('g2 start... ')
        let res = yield 4
        // Call another generator function directly, here just get a generator
        // Just write an object here, no effect
        g1()
        console.log('g2 logger - 1', res)
        res = yield 5
        console.log('g2 logger - 2', res)
        res = yield 6
        console.log('g2 logger - 3', res)
        return 'g2 end... '
    }
    
    const g = g2()
    g.next() // A subsequent call to next appears equivalent to the G1 () statement being ignored
    // ...
    g.next() // A subsequent call to next appears equivalent to the G1 () statement being ignored
    
    Yield * g1()
    function* g2() {
        console.log('g2 start... ')
        let res = yield 4
        // If called like this, the run at this point will go inside the generator function
        // Execute the specific code of g1 functions in sequence
        res = yield* g1()
        // the result of res is the return value of g1
        console.log('g2 logger - 1', res)
        res = yield 5
        console.log('g2 logger - 2', res)
        res = yield 6
        console.log('g2 logger - 3', res)
        return 'g2 end... '
    }
    Copy the code

This concludes the iterator and generator related content and points to note, which is a long and convoluted…