Promise 异步请求串行&并行执行实现

5,227 阅读2分钟

今天面试遇到几个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();
    })
}