Error message here!

Hide Error message here!

忘记密码?

Error message here!

请输入正确邮箱

Hide Error message here!

密码丢失?请输入您的电子邮件地址。您将收到一个重设密码链接。

Error message here!

返回登录

Close

迭代和递归:一道面试题引发的思考(2)

夜暮sky 2020-04-09 17:22:19 阅读数:32 评论数:0 点赞数:0 收藏数:0

前文阅读:迭代和递归:一道面试题引发的思考

不说废话,直接上正文。

问题

已知信息如下,loadUrls用于控制多个异步请求,通过max限制同一时刻的并发请求数,其返回Promise,结果为所有urls的异步结果。有点类似于Promise.all,不过内部对最大并发数做了控制。

const urls = ['url1', 'url2', 'url3', 'url4','url5'];
function load(url){
return new Promise((resolve, reject) => {
resolve(`${url}result`);
})
}
function loadUrls(urls, max) { }

分析:每次最多同时有max个异步请求,有一个请求返回,补上下一个请求,以此类推。

关键信息

1)首先需要发max个请求;
2)只有一个请求结束,才会补上一个请求发出;
3)当urls中的url全部处理后,结束并返回所有异步结果。
很显然,这是个循环处理的问题。前面我们讨论过递归与迭代,不再赘述。

迭代和递归的选择

由于while循环是同步控制,顺序执行的;而上例是异步的,执行循环的时机由.then决定,因此这里我们选择递归实现。

明确循环体和出口条件

按上期制定的原则,接下来最重要的是明确出口条件以及循环体:
1)出口条件:urls的中url是否已全部处理完成;
2)循环体:一个请求返回后,load().then()中触发下一个url进行load
需要注意下,上例多了个条件:限制max个并发请求,这个放到循环函数中好像没法处理啊,怎么办呢?
加一条原则:和循环过程无关的条件提到函数外

基于上面的分析,对loadUrls进行改造:
1) 由于loadUrls需要返回Promise对象,因此第一步可先构造返回:

function loadUrls(urls, max) {
return Promise((resolve, reject) => {
//...
});
}

2) 再者由于loadUrls返回的Promise对象得到的结果是所有异步请求的结果,因此,需要其resolve:

function loadUrls(urls, max) {
return Promise((resolve, reject) => {
//启动max个请求,传入resolve,供执行结束返回结果
for(let i = 0; i < max; i++ ) {
loadNext(urls[i], i, resolve);
}
});
}

3) 递归函数loadNext:

function loadUrls(urls, max) {
//记录所有异步请求的结果
let result = [];
//记录已经发出请求的序号
let loadIndex = max - 1;
//递归函数,初始条件以提出
function loadNext(url, index, resolve) {
load(url).then((val) => {
result[index] = val;
//下一个待发起请求url的下标
loadIndex++;
//判断是否所有的url已发出请求
if(loadIndex < urls.length) {
//对下一个待请求的url进行处理
loadNext(urls[loadIndex], loadIndex, resolve);
}else {
//所有url处理完毕,进行resolve
resolve(result);
}
})}
return new Promise((resolve, reject) => {
for(let i = 0; i < max; i++ ) {
loadNext(urls[i], i, resolve);
}
})
}

执行结果:

loadUrls(urls, 2).then(i => console.log(i))
// ["url1result", "url2result", "url3result", "url4result", "url5result"]
总结

递归函数如果遇到和循环逻辑无关的,不要慌,试着提出函数外考虑,那么关于递归总结的三大原则:
1)出口条件
2)循环逻辑
3)初始逻辑(提出函数外执行)
迭代其实也是这三个原则,不过由于while可以简单理解为一个独立的循环函数,其参数为出口条件,因此其初始条件(如果有的话,并不需要提出函数外)。

版权声明
本文为[夜暮sky]所创,转载请带上原文链接,感谢
https://segmentfault.com/a/1190000022318373