async await | 青训营笔记

87 阅读3分钟

学完了迭代器和生成器以后async和await就可以再来深入研究一下 平时开发最常使用Promise的场景应该就是发送异步网络请求,一般我们会封装一个函数

const requestData = (url) => {
  return new Promise((resolve, reject) => {
    // 用定时器模拟一手异步请求
    setTimeout(() => {
      resolve(url)
    }, 1000)
  })
}

requestData("Allen").then(res => {
  console.log(res)		// Allen
})

获得Promise以后使用then就可以获取到结果 然后假设产品来加一个需求,我们这里需要把传入的url获得到的结果拼接上一段东西然后再发送一次请求,得到结果再拼接再发送一次请求,那代码就会变得很难看

requestData("Allen").then(res => {
  requestData(res + " is").then(res => {
    requestData(res + " smart").then(res => {
      console.log(res)		// Allen is smart
    })
  })
})

这种一直嵌套就称为回调地狱,代码可读性非常差 可以利用promise.then的返回规则,我们用promise把res包裹起来然后返回

requestData("Allen").then(res => {
  return requestData(res + " is")
}).then(res => {
  return requestData(res + " smart")
}).then(res => {
  console.log(res)
})

这样也可以,但是如果中间对res有进行处理代码的可读性依然很糟糕,这时候就需要使用生成器来进行优化了 封装一个生成器函数,方便控制函数

function* getData() {
  const prop1 = yield requestData("Allen")    // requestData返回一个promise
  const prop2 = yield requestData(prop1 + " is")
  const prop3 = yield requestData(prop2 + " smart")
  console.log(prop3)			// Allen is smart
}

const generator = getData()
generator.next().value.then(res => {       // 这里可以拿到yield返回的promise
  generator.next(res).value.then(res => {       // 在next里面传参数传递给下一个yield代码片段
    generator.next(res).value.then(res => {
      generator.next(res)
    })
  })
})

调用生成器的next可以拿到不同yield代码片段的返回值,利用这一点,第一个请求返回的是promise我们直接通过yield返回,通过value就可以拿到这个promise,然后调用他的then拿到promise的结果res,这个res我们不使用它而是继续将他往下一个请求传递,传递给yield后续代码片段的方法是调用生成器的next方法时传进去,这样就可以实现想要的效果,但是这样生成器部分的代码依然是回调地狱,如果要继续发送请求拼接或者别的操作那就还需要继续写,但是这种传递参数的操作是相同的,所以可以考虑封装一个通用函数,用递归来实现相同的效果

function execGenerator(genFn) {
  const generator = genFn()

  const exec = (lastRes) => {
    const result = generator.next(lastRes)		// 拿到generator.next()的结果
    if(result.done) {					// 通过它的done属性来判断有没有遍历完
      return result.value			// 如果遍历到最后一个就把结果返回就好了
    }
    result.value.then(res => {
      exec(res)							// 没有遍历完那就递归调用exec把这个promise的res传进去
    })
  }
  exec()			// 记得第一次要调用一次
}

execGenerator(getData)		// Allen is smart

这样的代码就非常美观,并且可以自动化执行,无论需要进行多少次请求或者操作我们都只需要调用我们的请求函数直接拿到结果就行了,不用去管生成器函数 在ES8中这种方案有了语法糖,就是async和await

async function getData() {
  const prop1 = await requestData("Allen")    // requestData返回一个promise
  const prop2 = await requestData(prop1 + " is")
  const prop3 = await requestData(prop2 + " smart")
  console.log(prop3)
}
getData()