乐趣区

关于javascript:javascript异步编程之generator生成器函数与asnycawait语法糖

Generator 异步计划

相比于传统回调函数的形式解决异步调用,Promise最大的劣势就是能够链式调用解决回调嵌套的问题。然而这样写仍然会有大量的回调函数,尽管他们之间没有嵌套,然而还是没有达到传统同步代码的可读性。如果以上面的形式写异步代码,它是很简洁,也更容易浏览的。

// like sync mode

try{const value1 = ajax('/api/url1')
  console.log(value1)
  const value2 = ajax('/api/url1')
  console.log(value2)
  const value3 = ajax('/api/url1')
  console.log(value3)
  const value4 = ajax('/api/url1')
  console.log(value4)
  const value5 = ajax('/api/url1')
  console.log(value5)
}catch(err){console.log(err)
}
  

ES2015 提供了 生成器函数(Generator Function)它与一般函数的语法差异在于,在 function 语句之后和函数名之前,有一个“*”作为生成器函数的标示符。

在咱们去调用生成器函数的时候他并不会立刻去执行这个函数,而是会失去一个生成器对象,直到咱们手动调用对象的 next 办法,函数体才会开始执行,咱们能够应用关键字yield 去向外返回一个值,咱们能够在 next 办法的返回值中去拿到这个值。另外再返回的属性中还有一个 done 关键字来示意生成器是否执行完了,

yield不会像 return 一样去完结函数的执行,只是暂停函数的执行,直到外接下一次调用 next 办法时才会持续从 yield 地位往下执行

function * foo () {console.log('start')
    yield 'foo'
}

const generator = foo()

const result = generator.next()

调用 next 办法的时候传入了参数的话,所传入的参数会作为 yield 关键字的返回值

function * foo () {console.log('start')
    // 我能够在这里接管 next 传入的参数
    const res = yield 'foo'
  console.log(res) // 这是我传入的参数
}

const generator = foo()

const result = generator.next('这是我传入的参数')
console.log(result) // {value: 'foo', done: false}

如果咱们调用了生成器函数的 throw 办法,这个办法会给生成器函数外部抛出一个异样

function * foo () {console.log('start')
  // 我能够在这里接管 next 传入的参数
  try {
    const res = yield 'foo'
    console.log(res) // 这是我传入的参数
  } catch (err) {console.log(err.message) // 抛出谬误
  }
}

const generator = foo()

const result = generator.next('这是我传入的参数')
console.log(result)

generator.throw(new Error('抛出谬误'))

利用生成器函数和 Promise 来实现异步编程的体验

function ajax(url) {return new Promise((resove, reject) => {var xhr = new XMLHttpRequest()
    xhr.open('GET', url)
    // 新办法能够间接承受一个 j 对象
    xhr.responseType = 'json'
    xhr.onload = function () {if (this.status === 200) {resove(this.response)
      } else {reject(new Error(this.statusText))
      }
    }
    xhr.send()})
}

function* main() {const user1 = yield ajax('/json1.json')
  console.log(user1)

  const user2 = yield ajax('/json2.json')
  console.log(user2)

  const user3 = yield ajax('/json3.json')
  console.log(user3)
}

const g = main()
const result = g.next()

result.value.then(data => {const result2 = g.next(data)

  if (result2.done) return
  result2.value.then(data2 => {const result3 = g.next(data2)

    if (result3.done) return
    result3.value.then(data3 => {g.next(data3)
    })
  })
})

很显著生成器的执行器能够应用递归的形式去调用

const g = main()

function handleResult(result) {if (result.done) return
  result.value.then(data => {handleResult(g.next(data))
  }, err => {g.throw(err)
  })
}

handleResult(g.next())

生成器函数的调用其实都是差不多的,所以咱们能够写一个比拟通用的执行器

function co(generator) {const g = generator()

  function handleResult(result) {if (result.done) return
    result.value.then(data => {handleResult(g.next(data))
    }, err => {g.throw(err)
    })
  }

  handleResult(g.next())
}


co(main)

当然这样的执行器在社区中曾经有一个比较完善的库了 co。这种 co 的计划在 2015 年之前是特地风行的,起初在出了 async/await 语法糖之后,这种计划绝对来讲就没有那么遍及了。应用 generator 这种办法最显著的变动就是异步调用回归到扁平化了

async/await

有了 generator 之后 js 异步编程基本上与同步代码有相似的体验了,然而应用 generator 这种异步计划还须要本人手动去写一个执行器函数,会比拟麻烦。在 ES2017 的版本中新增了一个叫做 async 的函数,它同样提供了这种扁平化的编程体验,并且是语言层面的规范的异步编程语法。其实 async 函数就是生成器函数更不便的语法糖,所以语法上给 generator 函数是相似的。

async function main() {
  try {const user1 = await ajax('/json1.json')
    console.log(user1)

    const user2 = await ajax('/json2.json')
    console.log(user2)

    const user3 = await ajax('/json3.json')
    console.log(user3)
  } catch (error) {console.log(error)
  }
}

main()

async 函数返回一个 Promise 对象,更利于对整体代码管制

promise.then(() => {console.log('all completed')
}).catch(err => {console.log(err)
})

原文地址: https://kspf.xyz/archives/21
更多内容微信公众号搜寻 充饥的泡饭
小程序搜一搜 开水泡饭的博客

退出移动版