Why is it better to use promise than simple callback functions in asynchronous code?

When using promise.race to perform many long-running tasks, when will promises finally become resolved?

When will it fail to become resolved?

6.2 Generator Functions

Calling a generator does not execute the generator function; it creates an object called an iterator.

function* WeaponGenerator() {
  yield 'one'
  yield 'two'
  yield 'three'
}

for (let weapon of WeaponGenerator()) {
  console.log(weapon)
}

// one
// two
// three
Copy the code

6.2.1 Control of generators through iterator objects

const interator = WeaponGenerator()
interator.next() // {value: 'one', done: false}
interator.next() // {value: 'two', done: false}
interator.next() // {value: 'three', done: false}
interator.next() // {value: undefined, done: true}

let item
while(! (item = interator.next()).done) { item.value &&console.log(item.value)
}
Copy the code

Give execution to the next generator

function * W() {
  yield 'a'
  yield * N()
  yield 'd'
}

function * N() {
  yield 'b'
  yield 'c'
}

for(let w of W()) {
  console.log(w)
}

// a b c d
Copy the code

6.2.2 Using generators

  1. Generate a sequence of ids with a generator

    function * IdGenerator() {
            let id = 0
            while(true) {
                    yield ++id
            }
    }
    Copy the code

    An infinite loop should not normally be written in standard functions. But that’s fine in generators! When the generator encounters a yield statement, it suspends execution until the next method is called, so only once each time the next method is called will the while loop iterate once and return the next ID value.

  2. Use iterators to traverse the DOM tree

    function * DomTraversal(el) {
      yield el;
      el = el.firstElementChild
      while(el) {
        yield * DomTraversal(el)
        el = el.nextElementSibling
      }
    }
    
    const subTree = document.getElementById('subTree')
    for (let element ofDomTraversal(subTree)) { element ! = =null && console.log(element.nodeName)
    }
    Copy the code

6.2.3 Interacting with the generator

You can pass values to next()

If you pass an argument to the generator’s next() method, that value becomes the value returned by the generator’s current yield operation.

The Generator provides a lot of control between the yield operator in the Generator’s code path and the ability to specify a new starting value by passing it to generator.prototype.next ().

function * Gen(name) {
  let imp = yield('Hello ' + name)
  yield imp
}

const Iterator = Gen('World') // The first call does not record anything because the generator does not initially produce any results.
console.log(Iterator.next()) // {value: 'Hello World', done: false}
console.log(Iterator.next('Debra')) // {value: 'Debra', done: false}
Yield ('Hello '+ name) === = 'Debra'
Copy the code

6.2.4 Explore the internal composition of the generator

  1. Suspended Start – When a generator is created, it first starts in this state. None of the code is executed.
  2. Execute — The code in the generator has executed. Execution either begins or continues from the last time it was suspended. The generator moves to this state whenever the iterator corresponding to the generator calls the next method and there is currently executable code.
  3. Suspend assignment – When the generator encounters a yield expression during execution, it creates a new object containing the return value and then suspends execution. The generator pauses in this state and waits for execution to continue.
  4. Completed – During generator execution, the generator enters this state if the code executes until a return statement or if all code completes.

6.3 the use of Promise

const promiseDelay = new Promise((resolve, reject) => {
  console.log('promise is delay')
  setTimeout(() => {
    console.log('do then')
    resolve('one')
  }, 1000)
})

promiseDelay.then(res => {
  console.log(res, 'is done')
})
Copy the code

Only when the resolve is performed, the Pomise will reach the depressing state

6.3.5 Chained Use Promise

Return both SUCCESS and Success again

const promiseDelay = new Promise((resolve, reject) = > setTimeout(resolve, 1000.'success'))

promiseDelay.then(res= > {
  console.log(res)
  return 'success again'
}).then(res= > {
  console.log(res)
})
Copy the code

Return SUCCESS and Success Again at 1 second intervals

const promiseDelay = new Promise((resolve, reject) = > setTimeout(resolve, 1000.'success'))

promiseDelay.then(res= > {
  console.log(res)
  return new Promise((resolve, reject) = > setTimeout(resolve, 1000.'success'))
}).then(res= > {
  console.log(res)
})

The return of the first THEN cannot directly return promiseDelay because the promise is already in the fulfill state and will execute the next THEN
Copy the code

6.3.6 Waiting for Multiple Promises

Then is entered only after all of them succeed, and catch is entered directly after one failure

const promiseDelay1 = new Promise((resolve, reject) = > setTimeout(resolve, 1000.'1 success'))
const promiseDelay2 = new Promise((resolve, reject) = > setTimeout(resolve, 2000.'2 success'))
const promiseDelay3 = new Promise((resolve, reject) = > setTimeout(resolve, 500.'3 success'))

Promise.all([promiseDelay1, promiseDelay2, promiseDelay3])
  .then(([r1, r2, r3]) = > {
    console.log(r1, r2, r3)
  })

Return after 2 seconds => 1 SUCCESS 2 SUCCESS 3 Success

// As long as there is a fail
const promiseDelay1 = new Promise((resolve, reject) = > setTimeout(resolve, 1000.'1 success'))
const promiseDelay2 = new Promise((resolve, reject) = > setTimeout(resolve, 2000.'2 success'))
const promiseDelay3 = new Promise((resolve, reject) = > setTimeout(reject, 500.'3 fail'))

Promise.all([promiseDelay1, promiseDelay2, promiseDelay3])
  .then(([r1, r2, r3]) = > {
    console.log(r1, r2, r3)
  }).catch(e= > {
    console.log(e) // Return this fail after 500 milliseconds
  })
Copy the code

6.3.7 promise competition

Execute only the results that return the fastest, whether fail or SUCCESS

const promiseDelay1 = new Promise((resolve, reject) = > setTimeout(reject, 1000.'1 fail'))
const promiseDelay2 = new Promise((resolve, reject) = > setTimeout(resolve, 2000.'2 success'))
const promiseDelay3 = new Promise((resolve, reject) = > setTimeout(resolve, 500.'3 success'))

Promise.race([promiseDelay1, promiseDelay2, promiseDelay3])
  .then((result) = > {
    console.log(result) // 3 success
  }).catch(e= > {
    console.log(e)
  })
Copy the code

6.4 Generator + Promise

(I’m not sure what the meaning of this example is either.)

const promiseDelay1 = new Promise((resolve, reject) = > setTimeout(reject, 1000.'1 fail'))
const promiseDelay2 = new Promise((resolve, reject) = > setTimeout(resolve, 2000.'2 success'))
const promiseDelay3 = new Promise((resolve, reject) = > setTimeout(resolve, 500.'3 success'))

const iteratorFn = function* () {
  try {
    yield promiseDelay1
    yield promiseDelay2
    yield promiseDelay3
  } catch (e) {
    console.log(e)
  }
}

function asyncFn(generator) {
  const iterator = generator()

  function handle(result) {
    if (result.done) return
    const iteratorVal = result.value
    if (iteratorVal instanceof Promise) {
      iteratorVal.then(res= > {
        handle(iterator.next(res))
      }).catch(e= > iterator.throw(e)) // fail is not executed anymore}}try {
    handle(iterator.next())
  }
  catch (e) { iterator.throw(e) }
}

asyncFn(iteratorFn)
Copy the code

async

By using the keyword async before the keyword function, you can indicate that the current function depends on a value returned by an async. At each location where an asynchronous task is invoked, an await keyword is placed to tell the JS engine to wait for the result of execution at that location without blocking the execution of the application.Copy the code
async function asyncFn() {
  try {
    const pms1 = await promiseDelay1
    const pms2 = await promiseDelay2
    const pms3 = await promiseDelay3

    console.log(pms1)
    console.log(pms2)
    console.log(pms3)
                // Return at the same time
  } catch(e) {
    console.log('Error:', e) // If there is an error, it will return an error.
  }
}

asyncFn()
Copy the code

summary

  • A generator is a function that does not print all sequences of values at the same time, but instead generates values based on each request.
  • Unlike standard functions, generators can suspend and restore their execution state. When the generator generates a value, it suspends execution without blocking the main thread and then quietly waits for the next request.
  • Generators are defined by adding an asterisk (*) after function. Within the generator function body, we can use the new yield keyword to generate a value and suspend the generator execution. If we want to transfer to another generator, we can use the yield operator.
  • In our control of generator execution, it creates an iterator object by calling a generator using the iterator’s next method. In addition, we can pass values into the generator through the next function.
  • A promise is a placeholder for the computed value, a guarantee that we will eventually get an asynchronous computed result. Promises can fail as well as succeed. Once they are made, they cannot be changed.
  • Promises significantly simplify how we deal with asynchronous code. By using the THEN method to generate the promise chain, we can easily handle asynchronous timing dependencies. It is equally easy to execute multiple asynchronous tasks in parallel: just use the promise.all method.
  • By combining generators and promises we can use synchronous code to simplify asynchronous tasks.

practice

function * EvenGenerator() {
  let num = 2
  while (true) {
    yield num
    num += 2}}let generator = EvenGenerator()

let a1 = generator.next().value / / 2
let a2 = generator.next().value / / 4
let a3 = EvenGenerator().next().value / / 2
let a4 = generator.next().value / / 6
Copy the code
function * NinjaGenerator() {
  yield 'ninja1'
  yield 'ninja2'
  yield 'ninja3'
}

const ninjas = []
for (let ninja of NinjaGenerator()) {
  ninjas.push(ninja)
}

console.log(ninjas) // ['ninja1', 'ninja2', 'ninja3']

// Implement for of with while
**const ninjas = []
const generator = NinjaGenerator()
let ninja = generator.next()
while(! ninja.done) { ninjas.push(ninja.value) ninja = generator.next() }** If "NinjaGenerator" contains return, all NinjaGenerator characters (including return) are not executed
Copy the code
  1. To participate to the next ()

    In addition to being used as an intermediate return statement of a function, the yield keyword can also be used as an intermediate argument to a function. The yield keyword that paused the generator function the last time receives the first value passed to the next() method.

    The tricky part here is that the value passed in from the first call to next() is not used because the call is to start the execution of the generator function.

    function * Gen(val) {
      val = yield val *2 
      yield val
    }
    let generator = Gen(2)
    let a1 = generator.next(3).value // 4 The parameter 3 passed here is ignored because this call is to start the execution of the generator function
    let a2 = generator.next(5).value The value 5 is used as yield val * 2 and assigned to val
    Copy the code
    function *Gen(val) {
      val = yield val + 2
      yield val
      yield val
    }
    
    let generator = Gen(2)
    console.log(generator.next()) // 4 It doesn't matter whether the first pass is passed or not
    console.log(generator.next(9)) // 9 where 9 is assigned to val as yield val + 2
    console.log(generator.next(5)) // 9 passes 5 without assigning to val, val is still 9
    Copy the code
    function *Gen(val) {
      val = yield val + 2
      val = yield val
      yield val
    }
    
    let generator = Gen(2)
    console.log(generator.next()) / / 4
    console.log(generator.next(9)) / / 9
    console.log(generator.next(5)) / / 5
    Copy the code
const promise = new Promise((resolve, reject) = > {
  resolve(1) // The Promise state is complete, reject is no longer called
  setTimeout(() = > reject(2), 500)
})

promise
  .then(val= > console.log('Success:', val)) // Only Success: 1 will be printed
  .catch(e= > console.log('Error:', e)) // Will not be called to
Copy the code