在开发过程当中,遇到一个需求:在系统初始化时经过http获取一个第三方服务器端的列表,第三方服务器提供了一个接口,可经过分页形式获取列表。javascript
这里有两个问题: java
假设该接口为:node
GET http://www.example.com/api/list
QUERY page=PAGE_INDEX&size=PAGE_SIZE
RETURN {data: [DATA,...], page: {page: PAGE_INDEX, size: PAGE_SIZE}}复制代码
指望结果:
全部的数据面试
[DATA, ...]复制代码
借以该问题,了解关于递归的更多知识。api
callback + 辅助变量数组
let request = require('superagent')
let api = 'http://www.example.com/api/list'
function getList (page, size, data = [], cb) {
request(api).query({page, size})
.then(({body}) => {
if (body.data && body.data.length) {
// getList(page++, size, data.concat(body.data), cb)
getList(++page, size, data.concat(body.data), cb)
} else {
cb(data)
}
})
}
getList(1, 15, [], (data) => {
console.log(data)
})复制代码
仍是可以理解,不断请求,并把得到的数据传到下一个请求;直到返回数据为空时终止,并返回累加的数据。就算不用promise而是普通回答(如使用end方法)也是能够的,能够不依赖函数返回值。可是须要一个没有意义的变量。promise
promise服务器
let request = require('superagent')
let api = 'http://www.example.com/api/list'
function getList (page, size) {
return request(api).query({page, size})
.then(({body}) => {
if (body.data && body.data.length) {
return getList(++page, size)
.then((data) => {
return body.data.concat(data)
})
} else {
return []
}
})
}
getList(1, 15).then((data) => {
console.log(data)
})复制代码
相比较上面的方法,不在须要辅助变量和回调函数,看起来更像普通的递归。可是因为promise中不断return,使得代码难以理解,且容易忽略掉数据为空时else判断。异步
generater async
let request = require('superagent')
let co = require('co')
let api = 'http://www.example.com/api/list'
function* getList (page, size) {
let {body} = yield request(api).query({page, size})
if (body.data && body.data.length) {
return body.data.concat(yield getList(++page, size))
} else {
return []
}
}
co(function *() {
let data = yield getList(1, 100)
console.log(data.length);
})复制代码
同步方式的代码,更加方便阅读和理解。可是复杂的generater函数须要使用相应的环境才能运行,如co,最不喜欢的就是在数组的一些遍历方法中不能使用。
async + await 和generater差很少,也须要相应的环境,可是能够在数组的遍历方法中使用。
对于一下这句话,我老是抱着怀疑的,由于不少时候我会很快想到使用递归能够解决,而不会想到使用循环来解决。在JavaScript中,那些异步的递归函数怎么才能转换为循环呢?
递归和循环二者彻底能够互换
在一次面试中,面试官给我描述了这么一个需求:
一个函数指望经过输入两个数,返回一个数组,且不能使用循环。第一个参数x表示结果数组中元素,第二个参数n表明数组长度。
我第一反应就是使用递归,或者使用数组的遍历方法。说真的,我真的没有那么一丢丢的想到循环,然而循环貌似最应该容易想到...。
// 循环
function repeat (x, n) {
let arr = []
while (arr.length < n) {
arr.push(x)
}
return arr
}
// 递归
function repeat (x, n) {
return n > 0 ? [x].concat(repeat(x, --n)) : []
}
// 数组
function repeat (x, n) {
new Array(n).fill(x)
}复制代码
那么像上面那个请求的异步递归,若是没有ECMScript 2015(使用回调),该怎么写成循环呢?
经典广告词:想不出来