在前端异步请求中,要获取数据,传统的写法是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() //运行
以上代码仅完成了核心功能,一些防护性和异常处理不完善,仅供理解和学习