在前端異步請求中,要獲取數據,傳統的寫法是ajax回調,但這種方法
不利於代碼的維護和可讀性,因此es6時代經過Generator和Promise解決了這種問題,es7更是經過async和await使其更加簡潔前端
Generator的特色是初始化後,調用一次next方法會暫停在yield關鍵字前,同時也能夠在next方法裏傳值,使對應的yield語句獲取到
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
function call(url,options={}) { setTimeout(()=>{ if ( !it ) { throw new Error('請初始化生成器') } http(url,options) .then(v=>{ it.next(v.data) // 生成器傳遞參數,而且啓動下一次執行 }) },0) }
用setTimeout
包裹,是確保這段代碼是異步執行,若是不加,同步執行的it判斷可能會拋出異常es6
/** 多個請求互相依賴 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) }
it = getData() it.next()
這裏給it附上值,而後會觸發第2步的代碼ajax
首先咱們定義了生成器,運行它的時候,只需執行it.netx
,而後會運行第一個yield
後面的語句,並停在第一個yield語句處,當call函數裏的異步請求執行完畢,會將異步請求的結果it.next(data)
傳遞給第一個yield前面的取值語句,而後會執行到第二個yield
語句後面的call
,以此類推,直到整個生成器執行完畢json
單個經過Generator
已經能夠解決大部分異步嵌套的問題,可是不夠完善,要確保it初始化,必須讓整個call異步執行,代碼不夠優雅,並且依賴外部it,結構分散,因此咱們用Generator + Promise
能夠進一步完善axios
簡化call
方法app
function call(url,options={}) { return http(url,options) }
去掉在call裏執行it.next異步
增長外部調用生成器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()) //自運行 }
運行函數
run(getData)
運行方法一中的getData生成器,獲得的結果同樣
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裏寫一些異常,或者測試處理,相似React
的dva
處理方式**
若是你以爲方案二仍是有些繁瑣,那麼能夠試試ES7的await
語法
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
數據
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() //運行
以上代碼僅完成了核心功能,一些防護性和異常處理不完善,僅供理解和學習