JavaScript 异步代码处理方案演化过程

126 阅读2分钟

模拟网络请求

回调地狱问题

  • 这种代码阅读性极差
// 网络请求
function requsetDate(url) {
  return new Promise(resolve => setTimeout(() => resolve(url), 2000))
}

// 回调地狱
requsetDate('https://neko')
.then(res => {
  requsetDate(res).then(res => {
    requsetDate(res + 'aimer').then(res => {
      requsetDate(res + '.com').then(res => {
        console.log(res) // https://nekoaimer.com
      })
    })
  })
})

优化方案

  • Promise then方法的返回值也是Promise, 那么我们可以return出去
  • 这种方式就要清晰不少,但依然不是我认为最好的处理的方案或者说是结果
// 网络请求
function requsetDate(url) {
  return new Promise(resolve => setTimeout(() => resolve(url), 2000))
}

requsetDate('https://neko')
.then(res => {
    return requsetDate(res + 'aimer')
})
.then(res => {
  return requsetDate(res + '.com')
})
.then(res => {
  console.log(res) // https://nekoaimer.com
})

Promise + generator 方案

  • 这种方案从 getData() 能够清晰的得出每次结果
// 网络请求
function requsetDate(url) {
  return new Promise(resolve => setTimeout(() => resolve(url), 2000))
}

// 这种结构每次网络请求就会看的非常清晰
function* getData() {
  const res1 = yield requsetDate('https://neko')
  console.log(res1) // https://neko

  const res2 = yield requsetDate(res1 + 'aimer') 
  console.log(res2) // https://nekoaimer

  const res3 = yield requsetDate(res2 + '.com') 
  console.log(res3) // https://nekoaimer.com
}

// generator
const generator = getData()
generator.next().value.then(res => {
  generator.next(res).value.then(res => {
    generator.next(res).value.then(res => {
      generator.next(res)
    })
  })
})
  • 那么有人可能会有疑问了,下面的 generator 不也是回调地狱吗,代码量看上去更麻烦了呀?
  • 别急,我会进行一些处理,之后就会发现这种处理方式结构清晰,可重复利用是非常优秀的

自动执行generator函数

// 网络请求
function requsetDate(url) {
  return new Promise(resolve => setTimeout(() => resolve(url), 2000))
}

// 封装了一个自动执行的函数
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()
}

// generator 方式代码清晰明了
function* getData() {
  const res1 = yield requsetDate('https://neko')
  const res2 = yield requsetDate(res1 + 'aimer')
  const res3 = yield requsetDate(res2 + '.com')
  console.log(res3) // https://nekoaimer.com
}

execGenerator(getData)

co(TJ)

  • 安装 co 包
npm install co
  • 这个包实现的功能与上面的类似 所以可直接引入进来
function requsetDate(url) {
  return new Promise((resolve, reject) => {
    setTimeout(() => resolve(url), 2000)
  })
}

function* getData() {
  const res1 = yield requsetDate('https://neko')
  const res2 = yield requsetDate(res1 + 'aimer')
  const res3 = yield requsetDate(res2 + '.com')
  console.log(res3) // https://nekoaimer.com
}

const co = require('co')
co(getData)

async & await 实现

  • 这种方案本质上是 generator 与 Promise 的语法糖
  • 所以最终的解决方案就是下面这种了
function requsetDate(url) {
  return new Promise((resolve, reject) => {
    setTimeout(() => resolve(url), 2000)
  })
}

async function getData() {
  const res1 = await requsetDate('https://neko')
  const res2 = await requsetDate(res1 + 'aimer')
  const res3 = await requsetDate(res2 + '.com')
  console.log(res3) // https://nekoaimer.com
}
getData()