异步请求回调嵌套解决方案

前言

在前端异步请求中,要获取数据,传统的写法是ajax回调,但这种方法
不利于代码的维护和可读性,因此es6时代经过Generator和Promise解决了这种问题,es7更是经过async和await使其更加简洁前端

经过Generator替代回调嵌套

Generator的特色是初始化后,调用一次next方法会暂停在yield关键字前,同时也能够在next方法里传值,使对应的yield语句获取到
  1. 首先编写准备代码
const axios = require('axios')

const http = axios.create({
  baseURL: 'http://127.0.0.1:89',
  timeout: 3000,
  headers: {'Accept': 'application/json'}
});


let it;

定义的it是迭代器的意思,如今尚未值ios

  1. 异步请求调用方法
function call(url,options={}) {
    setTimeout(()=>{
        if ( !it ) {
            throw new Error('请初始化生成器')
        }
        http(url,options)
        .then(v=>{
            
            it.next(v.data)  // 生成器传递参数,而且启动下一次执行
        })
    },0)
}

setTimeout包裹,是确保这段代码是异步执行,若是不加,同步执行的it判断可能会抛出异常es6

  1. 编写生成器,这里是主要的异步请求逻辑处理
/**
    多个请求互相依赖
    1. 根据用户名,密码获取用户凭证
    2. 根据用户凭证获取用户数据id列表
    3. 获取数据列表中第一条数据详情
*/
function * getData() {
    const data1 = yield call('/login', { method: 'POST', data: JSON.stringify({user:'root',pass:'123456'}) })
    console.log('我是结果1:',data1)
    const data2 = yield call('/list', { method: 'POST', headers: { token: data1.data.token } })
    console.log('我是结果2:',data2)
    const data3 = yield call('/item', { method: 'POST', headers: { token: data1.data.token }, data: JSON.stringify({ id:data2.data.ids[0] }) })
    console.log('我是结果3:',data3)  
}
  1. 调用,运行
it = getData()
it.next()

这里给it附上值,而后会触发第2步的代码
图片描述ajax

运行流程

首先咱们定义了生成器,运行它的时候,只需执行it.netx,而后会运行第一个yield后面的语句,并停在第一个yield语句处,当call函数里的异步请求执行完毕,会将异步请求的结果it.next(data)传递给第一个yield前面的取值语句,而后会执行到第二个yield语句后面的call,以此类推,直到整个生成器执行完毕json

Generator + Promise

单个经过Generator已经能够解决大部分异步嵌套的问题,可是不够完善,要确保it初始化,必须让整个call异步执行,代码不够优雅,并且依赖外部it,结构分散,因此咱们用Generator + Promise能够进一步完善axios

  1. 简化call方法app

    function call(url,options={}) {
      return http(url,options)
    }

去掉在call里执行it.next异步

  1. 增长外部调用生成器next函数runasync

    function run (g) {
      const it = g();  // 初始化生成器, 注意这里的冒号
      
      (function each(res) {
          // 根据生成器的返回结果进行判断
          if (!res.done && res.value instanceof Promise ) {  // 若是是Promise返回
              res.value.then(v=>{
                  each( it.next(v.data) )    // 这里是方案一的call里的next并传值到下一次next 
              })
          } else if (res.done) {    // 生成器执行结束, 运行结束
              return
          } else {
              throw new Error('yield 后面请用返回Promise的函数')
          }
      })(it.next())    //自运行
    }
  2. 运行函数

    run(getData)

运行方法一中的getData生成器,获得的结果同样

  1. getData里yield后的函数扩展

根据run函数可知,只要it.next返回的结果是Promise便可正常运行,那么在getData里以下写法也是能够的

function * getData() {
  const data1 = yield http('/login', { method: 'POST', data: JSON.stringify({user:'root',pass:'123456'}) })
  console.log('我是结果1:',data1)
  const data2 = yield http('/list', { method: 'POST', headers: { token: data1.data.token } })
  console.log('我是结果2:',data2)
  const data3 = yield http('/item', { method: 'POST', headers: { token: data1.data.token }, data: JSON.stringify({ id:data2.data.ids[0] }) })
  console.log('我是结果3:',data3)  
}

**这里的http函数是axios的一个实例,返回值为Promise,
外层加call是能够在call里写一些异常,或者测试处理,相似Reactdva处理方式**

ES7处理方式

若是你以为方案二仍是有些繁琐,那么能够试试ES7的await语法

  1. 改造getData函数以下
/**
    多个请求互相依赖
    1. 根据用户名,密码获取用户凭证
    2. 根据用户凭证获取用户数据id列表
    3. 获取数据列表中第一条数据详情
*/
async function getData() {
    const { data:data1 } = await call('/login', { method: 'POST', data: JSON.stringify({user:'root',pass:'123456'}) })
    console.log('我是结果1:',data1)
    const { data:data2 } = await call('/list', { method: 'POST', headers: { token: data1.data.token } })
    console.log('我是结果2:',data2)
    const { data:data3 } = await call('/item', { method: 'POST', headers: { token: data1.data.token }, data: JSON.stringify({ id:data2.data.ids[0] }) })
    console.log('我是结果3:',data3)  
}

和方案二比较这个方法头部多了async关键字,去掉了*号,yield换成了await,这是ES7异步函数的声明方式。
注意返回值不是经过方案二中next res.data注入,因此获取到的是整个res,取值的时候注意拿结果里的.data数据

  1. 运行
getData()

结果和方案一二同样,这种方式更加简洁易懂

方案三简单完整代码

我的比较喜欢简洁有效的代码,因此推荐方案三

const axios = require('axios')

const http = axios.create({
  baseURL: 'http://127.0.0.1:89',
  timeout: 3000,
  headers: {'Accept': 'application/json'}
});


/**
    多个请求互相依赖
    1. 根据用户名,密码获取用户凭证
    2. 根据用户凭证获取用户数据id列表
    3. 获取数据列表中第一条数据详情
*/
async function getData() {
    const { data:data1 } = await http('/login', { method: 'POST', data: JSON.stringify({user:'root',pass:'123456'}) })
    console.log('我是结果1:',data1)
    const { data:data2 } = await http('/list', { method: 'POST', headers: { token: data1.data.token } })
    console.log('我是结果2:',data2)
    const { data:data3 } = await http('/item', { method: 'POST', headers: { token: data1.data.token }, data: JSON.stringify({ id:data2.data.ids[0] }) })
    console.log('我是结果3:',data3)  
}

getData() //运行

以上代码仅完成了核心功能,一些防护性和异常处理不完善,仅供理解和学习

相关文章
相关标签/搜索