今天面试遇到几个promise的问题做的不太好。
问题一
给定一个数组urls,里面保存着一组请求的url。通过调用一个getResponse(url)方法 发送异步请求。该方法返回值为一个promise。
var urls = ['url1','url2','url3','url4'];
const getResponse = (url)=>{
return new Promise((resolve,reject)=>{
console.log('参数为:',url)
setTimeout(()=>{
console.log('异步请求后结果为','afeter'+url);
resolve("success")
},1000)
})
}
实现两个方法,分别实现这些并行和串行的请求。
并行
并行很好实现,直接循环掉用即可,相当于自己实现一个promise.all方法,或者直接调用promise.all这个方法。
//模拟实现promise.all
const parallel = (arr)=>{
let result = new Array(arr.length).fill(null);
let count = 0;
return new Promise(( resolve,reject)=>{
for(let i = 0;i<arr.length;i++){
getResponse(arr[i])
.then(res=>{
result[i] = res;
count++;
if(count==arr.length){
resolve(result);
}
})
.catch(err=>{
reject(err);
})
}
})
}
串行
串行没想到好的解决办法,结束后百度出来一个利用reduce实现串行调用的方法。
方法1:
function serial(tasks){
var result = [];
return tasks.reduce((accumulator,item,index)=>{
return accumulator.then(res=>{
return getResponse(item).then(res=>{
result[index] = res
return index == tasks.length - 1 ? result : item
})
})
},Promise.resolve())
}
这种方法实际上和链式调用then是等效的,了解promise源码会知道,then实际上的作用是注册回调函数,而不是真的执行回调函数,回调函数是通过内部resolve调用的,因此上述reduce的实现思路实际上是提前注册好链式then的所有回调函数,然后串行执行所有的promise。
方法2:
受前面实现promise.all实现并行的启发,实际上我们只要不使用for循环执行多个promise,而是将执行函数抽象出来成为一个方法,然后通过一个变量记录当前已经执行完的请求数量,没执行完则继续递归调用即可:
const serial2=(tasks)=>{
let index = 0;
const resArr = [];
return new Promise((resolve,reject)=>{
const doTask = ()=>{
fecth(tasks[index])
.then(res=>{
index++;
resArr.push(res)
if(index==tasks.length){
return resolve(resArr);
}
doTask();
})
.catch(err=>{
return reject(err)
})
}
doTask();
})
}
问题二
实现retry:function retry(fn,times,delay)
fn为异步请求,经过retry包装后,首先执行fn,如果失败则没隔delay的时间尝试一次,直到最后失败。
function retry(fn,times,delay){
let time = 0;
let error = null;
return new Promise((resolve,reject)=>{
const attemp = ()=>{
Promise.resolve(fn)
.then(resolve)
.catch(err=>{
time++;
console.log("尝试失败");
if(time==times){
reject(err);
}else{
setTimeOut(()=>{
attemp();
},delay)
}
})
}
attemp();
})
}