異步請求回調嵌套解決方案

前言

在前端異步請求中,要獲取數據,傳統的寫法是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() //運行

以上代碼僅完成了核心功能,一些防護性和異常處理不完善,僅供理解和學習

相關文章
相關標籤/搜索