“This is the 12th day of my participation in the First Challenge 2022. For details: First Challenge 2022.”

Hello everyone, I am L. In the last article, we introduced what generators are and how to use them

We know that generators are a special type of iterator, so we can use generators instead of iterators.

Generators are used instead of iterators

First we create an iterator.

function createArrayIterator(arr) {
  let index = 0
  return {
    next: function() {
      if (index < arr.length) {
        return { done: false.value: arr[index++] }
      } else {
        return { done: true.value: undefined }
      }
    }
  }
}

const names = ['abc'.'cba'.'nba']
const namesIterator = createArrayIterator(names)
console.log(namesIterator.next());
console.log(namesIterator.next());
console.log(namesIterator.next());
console.log(namesIterator.next());
Copy the code

Next we transform this iterator function into a generator function. Calling the generator function produces a generator object that, like the iterator object, can call the next method.

function* createArrayIterator(arr) {
  yield 'abc'
  yield 'cba'
  yield 'nba'
}

Copy the code

But it’s not universal, and if you switch arrays, this function doesn’t work. We can transform it into a universal function.

function* createArrayIterator(arr) {
  for(const item of arr) {
    yield item
  }
Copy the code

In addition to the above notation, we can also use Yeild * to produce an iterable. It is equivalent to the grammar sugar above. Note that yeild* must be followed by the iterable. Yeild * iterates over the iterable one value at a time.

function* createArrayIterator(arr) {
  yield* arr
}
Copy the code

As an example, we want to implement a function that takes two arguments and iterates over numbers in this range.

function createRangeIterator(start, end) {
  let index = start
  return {
    next: function () {
      if (index < end) {
        return { done: false.value: index++ }
      } else {
        return { done: true.value: undefined }
      }
    }
  }
}

const rangeIterator = createRangeIterator(10.20)
console.log(rangeIterator.next());
console.log(rangeIterator.next());
console.log(rangeIterator.next());
console.log(rangeIterator.next());
console.log(rangeIterator.next());
Copy the code

We can transform this function into a generator function.

function* createRangeIterator(start, end) {
  let index = start
  while(index < end) {
    yield index++
  }
}
Copy the code

In this iterator summary article, we looked at iterating custom classes, using the [symbol. iterator] method to make objects iterable by default. Now let’s turn it into a generator.

class Classroom {
  constructor(address, name, students) {
    this.address = address
    this.name = name
    this.students = students
  }
  entry(newStudent) {
    this.students.push(newStudent)
  }
  /* foo = function() { console.log('foo'); } * /
  /* [Symbol.iterator] = function*() { yield* this.students } */* [Symbol.iterator]() {
    yield* this.students
  }
}

const classroom = new Classroom("3 big"."1102"["abc"."cba"])
// classroom.foo()
for (const item of classroom) {
  console.log(item);
}
Copy the code

We learned that generators can be used instead of iterators, which makes the code much cleaner, so we tried to use generators.

Asynchronous processing scheme

Now there is a demand, we send url as a parameter to get the data network request (url), and obtain the data to the triple stitching on the string and then after mosaics of the string as a parameter to send request to get the data network, access to the data splicing on string BBB again, and then after mosaics of the string as a parameter to send web request, Finally get the data.

function requestData(url) {
  // The code for asynchronous requests is put into executor
  return new Promise((resolve, reject) = > {
    // Simulate a network request
    setTimeout(() = > {
      // Get the result of the request
      resolve(url)
    }, 2000); })}Copy the code

The first option: multiple callbacks

requestData('haha').then(res= > {
  // Return a Promise
  return requestData(res + 'aaa')
}).then(res= > {
  return requestData(res + 'bbb')
}).then(res= > {
  console.log(res);
})
Copy the code

We can see callback functions nested within callback functions, creating callback hell that makes code unreadable and unmaintainable.

The second solution: the return value of then in the Promise

Please refer to this article for more information about Promise.

requestData('haha').then(res= > {
  // Return a Promise
  return requestData(res + 'aaa')
}).then(res= > {
  return requestData(res + 'bbb')
}).then(res= > {
  console.log(res);
})
Copy the code

The above code is still relatively unreadable.

The third scheme: Promise + Generator

For more information about generators, see the summary of generators in this article


function* getData() {
  const res1 = yield requestData('haha')
  const res2 = yield requestData(res1 + 'aaa')
  const res3 = yield requestData(res2 + 'bbb')
  console.log(res3);
}
const generator = getData()
generator.next().value.then(res= > {
  // res as the result of res1
generator.next(res).value.then(res= > {
  // res as the result of res2
  generator.next(res).value.then(res= > {
    console.log(res); })})})Copy the code

While the above code requires us to execute the generator function next manually over and over again, let’s encapsulate a function that executes automatically.

function execGenerator(genFn) {
  const generator = genFn()
  function exec(res) {
    const result = generator.next(res)
    if(result.done) {
      return result.value
    }
    result.value.then(res= > {
      exec(res)
    })
  }
  exec()
}

execGenerator(getData)
Copy the code

Of course, we usually do not write the above code, we can use the third-party package CO automatic execution, do not need to write the automatic execution function.

First install the package through NPM Install Co, and then import the package.

const co = require('co')
co(getData)
Copy the code

We can see how easy it is to get results using this package.

Scheme 4: async/await

We modify scheme 2: Promise + Generator to use async/await. We add async before function to change yield to await. Think of async/await as syntactic sugar for generator.

async function getData() {
  const res1 = await requestData('haha')
  const res2 = await requestData(res1 + 'aaa')
  const res3 = await requestData(res2 + 'bbb')
  console.log(res3);
}

getData()
Copy the code