Quickly grasp the relationship between ES6 iterator Generator and async and its usage

Keywords: Javascript axios Attribute

1. Traverser iterator

1.1 for traversal

First of all, how do you traverse an array when you first get out of js?

 var arr = [1, 2, 3, 4, 7, 8, 9]
    for (let i = 0;i < arr.length;i++) {
      console.log(arr[i]);
    }

1.2 forEach traversal

It looks like a dumb bunch, so ES5 has studied a foreach method for you, but it can't break, change the values of the array itself, or return any values.

      var arr = [1, 2, 3, 4, 7, 8, 9]
    var arr2 = arr.forEach((element, index) => {
      console.log('No.' + index + 'Numbers are:' + element);
      return element * 2;
    });
    console.log(arr2) // undefined
        console.log(arr1) // [1, 2, 3, 4, 7, 8, 9]

So foreach only gives you the most basic operations, whatever else. If you want to change your own values or have break and countinue operations, we can use the map operation instead. I wrote a blog summary before.

wzr:Summary of Array Traversal Methods

1.3 for-in traversal

ES6 then specifically provides a way to traverse arrays, for-of.When it comes to for-of, I have to mention for-in

So I wrote a blog about the difference between them, not here.

wzr:A deeper understanding of enumeration properties and for-in and for-of

It is worth mentioning that for-in can traverse arrays, but for-in is not recommended for traversing arrays. Why?Because enumerable attributes returned for -in are character types, not numeric types, if attributes such as "0", "1" and arrays 1,2 are added together, it is likely that they are not directly added, but rather string overlays.for example

const items = [1,2,3,4]

for(item in items){
  let tempitem = item+1 
  console.log(items[tempitem]) // undefined
  console.log(tempitem) // 01 21 32 41 item and number add result in string addition, so item above cannot be found
}

So to avoid ambiguity, it's better not to traverse the array with for-in.

1.4 for-of

Now you're on the topic, because for-in is harder to use on this side of the array, ES6 adds for-of to make up for it.This is a method of traversing arrays.Unlike forEach(), it supports break, continue, and return statements.And his own grammar is very simple:

for (variable of iterable) {
    //statements
}
  • Variable: In each iteration, the values of different attributes are assigned to the variable.

  • iterable: An object whose properties are iteratively enumerated.

The key problem is that for-of can not only traverse arrays, it can traverse many class array objects.

  • Array
  • Map
  • Set
  • String
  • TypedArray
  • arguments object of function
  • NodeList object

And his principle is that these class array objects all have an attribute, Symbol.iterator, that is, as long as he can traverse with Symbol.iterator, we take this attribute out alone and manually execute the next() method on our own to see that we have successfully traversed the array:

const items = [1,2,3,4]

const giao =  items[Symbol.iterator]()

console.log(giao.next()) // {value: 1, done: false}
console.log(giao.next()) // {value: 2, done: false}
console.log(giao.next()) // {value: 3, done: false}
console.log(giao.next()) // {value: 4, done: false}
console.log(giao.next()) // {value: undefined, done: true}

Similarly, we can learn more about his principles by writing an iterator manually:

Array.prototype.myiterator = function(){
  let i = 0 
  let items = this
  return {
    next(){
      const done  = i >= items.length
      const value = done ? undefined : items[i++]
      return {
        value, done
      }
    }
  }
}

const item = [1,2,3,4]

// Console
> const giao = item.myiterator() //When we get the traverser, we just need to execute myiterator instead of for-of to traverse the array.
> giao.next()
{value: 1, done: false}
> giao.next()
{value: 2, done: false}
> giao.next()
{value: 3, done: false}
> giao.next()
{value: 4, done: false}
> giao.next()
{value: undefined, done: true}

The effect is the same as for of.It is also worth noting that you can add this property to any object so that they can traverse through it.

const items = [ 'blue', 'yellow', 'white', 'black']

for(item of items){
  console.log(item)
}

1.5 Summary

If a traverser exists within an object, he can make it available for-of traversal by calling the next() method of the traverser until the done property becomes true.

2. Generator

Why talk about generator and traverser together, because generator functions essentially return a traverser!

The syntax of the generator is simple, with a * appended to the function and yielded to return the corresponding value.(You can actually think of yield as return, but you just need next() to make an external call, and there is one function that can only have one return, and yield can have more than one)

function* items(){
    yield "1"
    yield "2"
    yield "3"
}

const num = items()

// Console
> num.next()
{value: "1", done: false}
> num.next()
{value: "2", done: false}
> num.next()
{value: "3", done: false}
> num.next()
{value: undefined, done: true}
> num.next()
{value: undefined, done: true}

So we can also add operations before yield

function* items(){
    let i =0
    yield i // 0
    i++
    yield i // 1
    i++
    yield i // 2
    i++ //This one doesn't work because he's after yield
}

const num = items()

//Printing without a browser console is fine
console.log(num.next()) // {value:0,done:false}
console.log(num.next()) // {value:1,done:false}
console.log(num.next()) // {value:2,done:false}
console.log(num.next()) // {value:undefined,done:true}

With this feature, we can use Generator to wait for ajax.

function ajax(url){
  // The request succeeded in automatically calling the next() method.Then return the data results
  axios.get(url).then(res => gen.next(res.data))
}
function* step(){
  const class = yield ajax.get(`http://laotie.com/getclass`)
  const score = yield ajax.get(`http://laotie.com/getscore?name=${class[0].name})
}

// Get the traverser for this function
const gen = step()
// Launch Traverser without starting
gen.next() // Get class
gen.next() // Get score

Because the second get request relies on the results of the first request, the first solution is to use Promise's callbacks to limit their order, but after we learned the generator, we found that it was a good feature to do this.That is, the second request can be executed sequentially only after the first request has been executed.

There are other small features

* Can be added anywhere without affecting the generation of genterator s.The following are all possible.

function * foo(x, y) { ··· }
function *foo(x, y) { ··· }
function* foo(x, y) { ··· }
function*foo(x, y) { ··· }

Regarding the Thunk or co module of Generator, because the addition of async of ES8 greatly simplifies the operation of Genrtator, there are no more requirements for principle and actual operation, let's talk about the async function.

3.async

1. Grammar

To be precise, async is Generator's grammatical sugar. First look at his grammar.

async function laotie(){
  const data = await dosomething()
}

As you can see,

  • Original * Replaced by async
  • The original yield was replaced by await

The immediate benefit is that it appears more semantic and readable.But async actually does much more than that.

  1. The first point is that async doesn't have to keep executing next(). The async function has a built-in executor so that when we call a function, we only need to call it directly.The following:

    // Continue with a block of code
     laotie()
  2. Async's await now retains the ability to wait, but since there is no next(), the call to await will not return a value like yield.In async, only return returns, and a promise object is returned.

    Rewrite the above code directly to the format of async plus await,

    async function items(){
     let i =0
     await i 
     i++
     await i 
     i++
     await i 
     i++ 
    }
    
    console.log(items()) // Promise {<pending>}

    By calling the method directly, we can see that a Promise object with a resolved state is returned, not an Iterator.

    And this object, the value returned is the value returned in the function.We can use the then() function to accept this value and print it.

    async function items(){
     let i =3
     return i
    }
    
    items().then(res=>{
     console.log(res) // 3
    })

    Of course, such examples are certainly not formal, and they are mainly used to distinguish the difference between the Generator and the async functions.

2. Usage

The correct usage is now to add an asynchronous function after await, which is equivalent to converting the asynchronous function into a synchronous function, and to wait until the asynchronous function has been executed and returned to resolved before proceeding further.For example:

async function asyncPrint(value, ms) {
  await new Promise(resolve => {
    setTimeout(resolve, ms);
  });
  console.log(value);
}

asyncPrint('hello world', 1000); // Print hellow world in one second

If there is more than one await in the middle of this async function, let multiple awaits execute in a queue.

Usage 2:

Let's take the previous generator example first

function ajax(url){
  // The request succeeded in automatically calling the next() method.Then return the data results
  axios.get(url).then(res => gen.next(res.data))
}
function* step(){
  const class = yield ajax.get(`http://laotie.com/getclass`)
  const score = yield ajax.get(`http://laotie.com/getscore?name=${class[0].name})
}

// Get the traverser for this function
const gen = step()
// Start traverser
gen.next() 

Writing is tiring, but async can quickly simplify it.Because await accepts a Prmoise function, we can use axios directly behind await and then use object deconstruction directly to get the corresponding value.

async function step(){
  const {data:{class}} = await axios.get(`http://laotie.com/getclass`)
  const {data:{core}} = await axios.get(`http://laotie.com/getscore?name=${class[0].name})
    return {class,core}
}

Is that convenient?

Posted by scottjcampbell on Thu, 05 Mar 2020 08:44:54 -0800