-
Notifications
You must be signed in to change notification settings - Fork 3.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
第 153 题:实现一个批量请求函数 multiRequest(urls, maxNum) #378
Comments
// 我先来,模拟图片加载过程
function loadImg(url) {
return new Promise((resolve, reject) => {
const img = new Image();
img.onload = function() {
console.log(url, "加载完成");
resolve(img);
};
img.onerror = function() {
reject(new Error('Error at:' + url));
};
img.src = url;
})
}
function multiRequest(urls, maxNum) {
const firstMaxNum = urls.splice(0, maxNum);
let promises = firstMaxNum.map((url, index)=>{
return loadImg(url).then(()=>{
return index
})
})
return urls.reduce((res, cur)=>{
return res.then(()=>{
return Promise.race(promises)
}).then((idx)=>{
promises[idx] = loadImg(cur).then(()=>{
return idx
})
})
}, Promise.resolve()).then(()=>{
return Promise.all(promises)
})
}
multiRequest(urls, 4).then(()=>{
console.log('finish')
}) |
function multiRequest(urls, maxNum) {
const ret = [];
let i = 0;
let resolve;
const promise = new Promise(r => resolve = r);
const addTask = () => {
if (i >= arr.length) {
return resolve();
}
const task = request(urls[i++]).finally(() => {
addTask();
});
ret.push(task);
}
while (i < maxNum) {
addTask();
}
return promise.then(() => Promise.all(ret));
}
// 模拟请求
function request(url) {
return new Promise((r) => {
const time = Math.random() * 1000;
setTimeout(() => r(url), time);
});
} |
通过count计数的形式,判断接口是否全部完成
|
借鉴了一些题解的实现,用例跑通了,有问题或者可优化的话请各位大佬指正。 解题的关键是:队列和递归 代码如下 function handleFetchQueue(urls, max, callback) {
const urlCount = urls.length;
const requestsQueue = [];
const results = [];
let i = 0;
const handleRequest = url => {
const req = fetchFunc(url)
.then(res => {
results.push(res);
})
.catch(e => {
results.push(e);
})
.finally(() => {
const len = results.length;
if (len < urlCount) {
// 完成请求就出队
requestsQueue.shift();
handleRequest(urls[++i]);
} else if (len === urlCount) {
"function" === typeof callback && callback(results);
}
});
requestsQueue.push(req);
// 只要满足就继续请求
if (requestsQueue.length <= max) {
handleRequest(urls[++i]);
}
};
handleRequest(urls[i]);
} |
如果网络太好,建议将Network throttling 调成Fast 3G。 |
class Scheduler { const timeout = (time) => new Promise((resolve, reject) => { function multiRequest(urls = [], maxNum = 1) { |
这个不行吧,请求不是按顺序返回的,切要并发请求吧
res是请求链接,res.then鞋油问题吧 |
说白了,就是实现一个限制并发的 Promise.all |
目前设定的是,当执行过程中某个 promise 返回 reject 则停止后续的 promise 执行。 后续可以加上,abort 请求。可以是一个回调,失败时候把 executing 传递过去。回调去执行一些操作。 // Promise.all([promsie1(), promsie2(), promsie3()]);
// PromiseLimit([promsie1, promsie2, promsie3]);
function PromiseLimit(funcArray, limit = 5) {
let i = 0;
const result = [];
const executing = [];
const queue = function() {
if (i === funcArray.length) return Promise.all(executing);
const p = funcArray[i++]();
result.push(p);
const e = p.then(() => executing.splice(executing.indexOf(e), 1));
executing.push(e);
if (executing.length >= limit) {
return Promise.race(executing).then(
() => queue(),
e => Promise.reject(e)
);
}
return Promise.resolve().then(() => queue());
};
return queue().then(() => Promise.all(result));
} |
// 测试代码
const result = [];
for (let index = 0; index < 10; index++) {
result.push(function() {
return new Promise((resolve, reject) => {
console.log("开始" + index, new Date().toLocaleString());
setTimeout(() => {
resolve(index);
console.log("结束" + index, new Date().toLocaleString());
}, parseInt(Math.random() * 10000));
});
});
}
PromiseLimit(result).then(data => {
console.log(data);
}); 测试结果,图片不知道为啥不显示 开始0 2020/5/18 上午11:28:47 结束0 2020/5/18 上午11:28:47 结束5 2020/5/18 上午11:28:51 结束4 2020/5/18 上午11:28:53 结束7 2020/5/18 上午11:28:53 结束1 2020/5/18 上午11:28:54 结束3 2020/5/18 上午11:28:54 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] |
// 修改测试代码 随机失败或者成功
const result = [];
for (let index = 0; index < 10; index++) {
result.push(function() {
return new Promise((resolve, reject) => {
console.log("开始" + index, new Date().toLocaleString());
setTimeout(() => {
if (Math.random() > 0.5) {
resolve(index);
} else {
reject(index);
}
console.log("结束" + index, new Date().toLocaleString());
}, parseInt(Math.random() * 1000));
});
});
}
PromiseLimit(result).then(
data => {
console.log("成功", data);
},
data => {
console.log("失败", data);
}
); 开始0 2020/5/18 上午11:34:37 |
// 这题如果maxNum 为无限大,其实就是在让你实现Promise.all
// 如果是有一个失败就返回 就是Promise.race
function multiRequest(urls = [], maxNum) {
let result = new Array(urls.length).fill(false)
let sum = urls.length; //总数
let count = 0; //已完成数
return new Promise((resolve, reject) => {
//先请求maxNum个呗
while (count < maxNum) {
next()
}
function next() {
let current = count++
// 边界
if (current >= sum) {
!result.includes(false) && resolve(result)
return
}
let url = urls[current];
console.log("开始:" + current, new Date().toLocaleString());
fetch(url).then((res) => {
console.log("结束:" + current, new Date().toLocaleString());
result[current] = res
//还有未完成,递归;
if (current < sum) {
next()
}
}).catch((err) => {
console.log("结束:" + current, new Date().toLocaleString());
result[current] = err
if (current < sum) {
next()
}
})
}
})
}
let url2 = `https://api.github.com/search/users?q=d`;
let arr = new Array(100).fill(url2)
multiRequest(arr, 10).then((res) => {
console.log(res)
}) |
@SupermanWY const fetch = (url) => {
return new Promise((rs) => {
const s = Math.random() * 1000
setTimeout(() => {
rs(url)
}, s)
})
}
const multiRequest = (urls, maxNum) => {
let allPromise = []
let len = urls.length
let resolve
let indirectPromise = new Promise((rs) => {
resolve = rs
})
let i = 0
let addFetch = () => {
if (i >= len) {
return resolve()
}
let tempP = fetch(urls[i++]).finally(() => {
addFetch()
})
allPromise.push(tempP)
}
// 这里需要限定一下
while (i < maxNum && i < len ) {
addFetch()
}
return indirectPromise.then(() => {
return Promise.all(allPromise)
})
}
multiRequest([
'/a',
'/b',
'/c',
'/d',
'/e',
'/f',
], 40).then((res) => {
console.log(res)
})
|
function multiRequest(urls, maxNum) {
let i = 0
let res = []
return new Promise(r => {
for(; i < maxNum; i++) {
addTask()
}
function addTask() {
res[i] = fetch(urls[i])
res[i].then(res => { i >= urls.length && r();i < urls.length && addTask(); i++; })
}
}).then((_) => {
Promise.all(res).then(ans => {
console.log(ans)
})
})
}
function fetch(url) {
return new Promise((resolve) => {
let start = new Date()
setTimeout(() => {
resolve(`start: ${start};end: ${new Date()}`)
}, 10000 * Math.random());
})
}
multiRequest([1,2,3,4,5,6,7,8,9], 3) |
我第一感觉是:他让我写个promise.all |
const apiPath = 'http://localhost:3000/'; 后台koa启个node服务 大概是 }); |
function multiRequest(urls, maxNum = 8) { |
function request (url) {
return new Promise (resolve => {
const time = Math.random() * 1000
setTimeout(() => resolve(`${url} + ${Date.now()}`), time)
})
}
function maxNum (urls, maxNum) {
this.maxNum = maxNum
this.urls = urls
this.requestArr = []
this.requestQueue = []
this.result = []
this.renderQueue = function () {
this.requestQueue = []
this.requestArr = []
this.requestQueue = this.urls.splice(0, this.maxNum)
this.renderRequest()
}
this.renderRequest = function () {
this.requestQueue.forEach(item => {
this.requestArr.push(request(item))
})
Promise.all(this.requestArr).then(res => {
this.result = this.result.concat(res)
if (this.urls.length) {
this.renderQueue()
} else {
this.printRes()
}
})
}
this.renderQueue()
this.printRes = function () {
console.log(this.result, 'result')
return this.result
}
}
let urls = ['www.test.com/1', 'www.test.com/2', 'www.test.com/3']
maxNum(urls, 2) |
function multiRequest(urls, maxNum) {
return new Promise((resolve, reject) => {
const result = Array(urls.length);
let resNum = 0;
let promiseIndex = 0;
while (promiseIndex < maxNum) {
genNextFetch();
}
function genNextFetch() {
if (promiseIndex >= urls.length) return;
const curIndex = promiseIndex++;
fetch(urls[curIndex])
.then((res) => {
result[curIndex] = res;
resNum += 1;
if (resNum === urls.length) {
resolve(result);
} else {
genNextFetch();
}
})
.catch((err) => {
reject(err);
});
}
});
} |
/**
* @param {Array<string>} urls
* @param {number} maxNum
* @return {Promise<Array<any>>}
*/
function multiRequest (urls, maxNum) {
const tasks = urls.map(url => request(url));
const tasksLen = tasks.length;
const result = [];
return new Promise((resolve, reject) => {
const runTask = (index) => {
if (result.filter(item => item).length === tasksLen) {
return resolve(result);
};
const min = Math.min(tasks.length, maxNum);
for (let i = 0; i < min; i++) {
const task = tasks.shift();
task.then(res => {
result[index + i] = res;
}).catch(reject).finally(() => {
runTask(min);
});
}
}
runTask(0);
});
}
const urls = ['request 1', 'request 2', 'request 3', 'request 4', 'request 5'];
multiRequest(urls, 4).then(res => {
console.log('result: ', res);
console.log('finish...');
});
// 模拟请求
function request(url) {
return new Promise((resolve, reject) => {
const time = Math.random() * 5000;
setTimeout(() => resolve(url), time);
});
} |
function multiRequest(urls, maxNum) {
return new Promise((res, rej) => {
const tasksLength = urls.length
const result = []
let i = 0
let done = 0
function runTask() {
if(i >= tasksLength) return
const curIndex = i
const url = urls[curIndex]
fetch(url).then(res => res.json()).then((value) => {
result[curIndex] = value
done++
if(done === tasksLength) {
res(result)
return
}
runTask()
}).catch(err => {
rej(err)
})
i++
}
while(maxNum !== 0) {
runTask()
maxNum--
}
})
} |
@sheepshine 题目中的条件2、每当有一个请求返回,就留下一个空位,可以增加新的请求, 直接用promise.all应该做不到吧? |
async function multiRequest(urls, maxNum) {
let data = urls.map((url, index) => ({ index, url }))
let result = []
let promises = Array.from({ length: Math.min(maxNum,data.length) }, () => getChain(data, result))
await Promise.all(promises)
return result
}
async function getChain(data, res = []) {
while (data.length) {
let one = data.pop()
try {
let urlRes = await fetch(one.url)
res[one.index] = urlRes
}
catch (e) {
res[one.index] = e
}
}
}
// 调用
let arr = Array.from({ length: 100 }, () => 'https://www.baidu.com')
let finalRes = await multiRequest(arr, 5)
console.log('done', finalRes) |
const promise = new Promise(r => resolve = r); |
递归调用来实现,这个想法是,最初您发送的请求数量上限为允许的最大值,并且这些请求中的每一个都应该在完成时继续递归发送,通过传入的索引来确定了urls 里面具体是那个URL,保证最后输出的顺序不会乱,而是依次输出;
|
function multiRequest(urls = [], maxNum) {
// 请求总数量
const sum = urls.length;
// 根据请求数量创建一个数组来保存请求的结果
const result = new Array(sum).fill(false);
// 当前完成的数量
let count = 0;
return new Promise((resolve, reject) => {
// 请求maxNum个
while (count < maxNum) {
next();
}
function next() {
let current = count++;
// 处理边界条件
if (current >= sum) {
// 请求全部完成就将promise置为成功状态, 然后将result作为promise值返回
!result.includes(false) && resolve(result);
return;
}
const url = urls[current];
console.log(`开始 ${current}`, new Date().toLocaleString());
fetch(url).then(res => {
// 保存请求结果
result[current] = res;
console.log(`完成 ${current}`, new Date().toLocaleString());
// 请求没有全部完成, 就递归
if (current < sum) {
next();
}
}).catch(err => {
console.log(`结束 ${current}`, new Date().toLocaleString());
result[current] = err;
// 请求没有全部完成, 就递归
if (current < sum) {
next();
}
});
}
});
}
const url = `https://www.baidu.com/s?wd=javascript`;
const urls = new Array(100).fill(url);
(async () => {
const res = await multiRequest(urls, 10);
console.log(res);
})(); |
看了下题目,不知道我这种实现对不对?没有验证过: // 以 axios.post 为例
const multiRequest = (urls, maxNum) => {
return new Promise(resolve => {
let next = maxNum // 下一个准备发送的请求下标
let results = []
const p = (index) => {
axios.post(urls[index]).then(res => {
results[index] = res
const i = next
++next
if (i < urls.length) {
p(next)
}
if (results.length === urls.length) {
resolve(results)
}
})
}
for (let i = 0; i < urls.slice(0, maxNum); i++) {
p(i)
}
})
} |
实现/**
*
* @param {Array<() => any>} funcArray
* @param {number} limit 最大请求次数
*/
export function promiseLimit(funcArray, limit = 5) {
return new Promise((resolve) => {
let i = funcArray.length >= limit ? limit : funcArray.length
const result = []
function execFunc(func) {
if (i > funcArray.length) return resolve(result)
let p = func()
p = p instanceof Promise ? p : Promise.resolve(p)
p.then(
() => execFunc(funcArray[i++]),
() => execFunc(funcArray[i++])
)
result.push(p)
}
result.concat(funcArray.slice(0, i).map(func => execFunc(func)))
})
} 测试function testPromiseLimit(base) {
let a = []
for (let i = 0; i < base; i++) {
a.push(() => {
console.log(`开始 ${i}`)
return new Promise((resolve, reject) => {
Math.random() > 0.5 ? setTimeout(() => resolve('success'), i * 500) : setTimeout(() => reject('fail'), i * 500)
})
})
}
return a
}
promiseLimit(testPromiseLimit(3))
promiseLimit(testPromiseLimit(5))
promiseLimit(testPromiseLimit(20)) |
function multiRequest(urls, nums) {
let result = {}
function fetchData(url, t) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('result: ' + url)
}, t)
})
}
function request() {
return new Promise((resolve, reject) => {
for (let index = 0; index < urls.length; index++) {
const element = urls[index];
if (!result[element]) {
fetchData(element, 1000 * (index + 2)).then((r) => {
result[element] = r
if (Object.keys(result).length === urls.length) {
resolve(result)
}
})
}
}
})
}
function add(url) {
if (urls.length < nums) {
urls.push(url)
return request()
}
}
return{
add,
request
}
}
function test() {
let urls = ['a', 'b', 'c']
const nums = 5
const {add, request} = multiRequest(urls, nums)
function print(result) {
for (let index = 0; index < urls.length; index++) {
const element = urls[index];
console.log('index', index, 'url', element, 'result', result[element])
}
}
request().then(print)
setTimeout(() => {
add('d').then(print)
}, 4000)
}
test() |
function multiRequest(urls, maxNum){
let res = [], idx = 0, count = 0, len = urls.length;
return new Promise((resolve, reject)=>{
let next = function(){
maxNum--;
Promise.resolve(urls[idx++]).then(val=> {
res[count++] = {status: 'fulfilled', value: val};
}, err=> {
res[count++] = {status: 'rejected', value: err};;
}).finally(()=> {
if(idx<len){
maxNum++;
next();
}else{
resolve(res)
}
})
}
while(idx < len && maxNum > 0){
next()
}
})
} |
class Scheduler {
constructor(maxNum) {
//等待执行的任务队列
this.taskList = []
//当前任务数
this.count = 0
//最大任务数
this.maxNum = maxNum
}
async add(promiseCreator) {
//当当前任务数超出最大任务数就将其加入等待执行的任务队列
if (this.count >= this.maxNum) {
await new Promise(resolve => {
this.taskList.push(resolve)
})
}
this.count++
const result = await promiseCreator()
this.count--
//当其它任务执行完任务队列中还有任务没执行就将其出队并执行
if (this.taskList.length > 0) {
this.taskList.shift()()
}
return result;
}
}
// 模拟请求
function request(url) {
return new Promise((r) => {
const time = Math.random() * 1000;
setTimeout(() => r(url), time);
});
}
function multiRequest(urls, maxNum){
const requests = [];
const scheduler = new Scheduler(maxNum);
for(let i = 0, len = urls.length; i < len; i++) {
requests.push(scheduler.add(() => request(urls[i])))
}
Promise.all(requests).then((res) => res.forEach((r) => console.log(r)))
} |
function multiRequest(urls, maxNum) {
const waitUrls = [] // 等待执行的url队列
function fetchUrl(url) {
delay(url).then((res) => {
console.log('res: ', res)
if (waitUrls.length) {
fetchUrl(waitUrls[0])
waitUrls.shift()
}
})
}
urls.forEach((url, index) => {
if (index < maxNum) {
fetchUrl(url)
} else {
waitUrls.push(urls[index])
}
})
}
/**
* test
*/
function delay(result) {
return new Promise((resolve) => {
setTimeout(() => {
resolve(result)
}, 2000)
})
}
multiRequest(['xxx.json', 'yyy.json', 'zzz.json', 'qqq.json', 'www.json'], 2) |
function request(url) {
const timeOut = Math.random() * 10;
const result = url +': ' + timeOut + 's';
return new Promise((res, rej) => {
try {
setTimeout(() => {
// console.log(url, result);
res(result);
}, timeOut * 1000);
} catch (error) {
console.error(url, error);
rej(error);
}
});
}
function multiRequest(urls, maxNum = 3) {
let requestedIndex = 0;
let currentReqestting = 0;
const results = {};
function pushRequest(url, callback) {
if (requestedIndex >= urls.length || currentReqestting >= maxNum) {
return;
}
request(url)
.then((res) => {
results[url] = res;
currentReqestting--;
if (Object.keys(results).length >= urls.length) {
callback();
}
pushRequest(urls[requestedIndex], callback);
})
.catch(e => {
results[url] = e;
currentReqestting--;
if (Object.keys(results).length >= urls.length) {
callback();
}
pushRequest(urls[requestedIndex], callback);
});
currentReqestting++;
requestedIndex++;
pushRequest(urls[requestedIndex], callback);
}
return new Promise((res, rej) => {
try {
if(requestedIndex < urls.length && currentReqestting < maxNum){
pushRequest(urls[requestedIndex],() => {
res({
urls,
results
});
});
}
} catch (error) {
rej({
urls,
error
});
}
});
}
multiRequest(['/sale_center/sale_order/', '/sale_center/klicen_order/', '/sale_center/terminal_order/', '/sale_center/xyt_order/', '/sale_center/patch_order/'])
.then(({ urls, results }) => {
console.log('------------------------------');
console.log(results);
urls.forEach(url => {
console.log(results[url]);
});
}); |
function multiRequest(urls, maxNum, callback) {
var arr = [];
var count = 0;
for (let i = 0; i < maxNum; i++) {
ajax(i)
}
function ajax(i) {
if (urls[i] !== undefined && urls[i] !== null) {
setTimeout(function () {
count++;
success(urls[i], i)
}, Math.random() * 10000)
}
}
function success(res, i) {
arr[i] = res;
if (count === urls.length) {
callback(arr);
} else {
let index = maxNum + count - 1
ajax(index)
}
}
} |
function multiRequests(urls, maxNums) {
let total = urls.length;
let results = [];
return new Promise((resolve, reject) => {
let currIndex = 0; // 当前待执行的下标
let restNum = total; // 剩余未执行的数量
// 添加初始值
for (let i = 0; i < maxNums; i++) {
if (i < total) {
addRequest(request(urls[i]), i);
}
}
function addRequest(req, index) {
currIndex++;
req.then((res) => {
results[index] = res;
})
.finally(() => {
restNum--;
if (currIndex < total) {
let req = request(urls[currIndex]);
addRequest(req, currIndex);
} else if (restNum == 0) {
resolve(results);
}
});
}
});
} |
// 实现一个批量请求函数 multiRequest(urls, maxNum)
function multiRequest(urls, maxNum) {
const result = []
const start = +new Date()
let resolvedCount = 0
let pendingCount = 0
let windowSize = 0
function generateP(url, pendingCount) {
return new Promise((res, rej) => {
setTimeout(() => {
res({
data: url,
index: pendingCount,
});
}, url * 1000);
});
}
return new Promise((r) => {
while (windowSize < maxNum) {
execP()
}
function execP() {
const p = generateP(urls[pendingCount], pendingCount);
windowSize++;
pendingCount++;
p.then(({index, data}) => {
resolvedCount++;
windowSize--;
result[index] = {
data,
time: +new Date() - start,
};
if (resolvedCount === urls.length) {
r(result);
} else {
if (pendingCount < urls.length) {
execP();
}
}
});
}
});
}
var start = +new Date()
multiRequest([3, 1, 4, 2], 2).then((data) => {
console.log(`time passed ${+new Date() - start}`, data);
}) |
function myFetch(url) {
const waitTime = Math.random() * 100 + 100;
return new Promise((resolve) => {
setTimeout(() => {
console.log(url);
resolve(url);
}, waitTime);
});
}
function multiRequest(urls, maxNum) {
return new Promise((resolve) => {
const results = Array.from(urls, () => null);
let numSuccess = 0;
let waitIdx = maxNum;
function triggerNext() {
if (waitIdx < urls.length) {
const thisIdx = waitIdx;
myFetch(urls[thisIdx]).then((r) => {
results[thisIdx] = r;
numSuccess++;
triggerNext();
});
waitIdx++;
}
if (numSuccess === urls.length) resolve(results);
}
for (let i = 0; i < maxNum; i++) {
myFetch(urls[i]).then((r) => {
results[i] = r;
numSuccess++;
triggerNext();
});
}
});
}
multiRequest([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13], 6).then((r) =>
console.log(r)
); |
function myFetch(url) {
const waitTime = Math.random() * 100 + 100;
return new Promise((resolve) => {
setTimeout(() => {
console.log(url);
resolve(url);
}, waitTime);
});
}
function multiRequest(urls, maxNum) {
return new Promise((resolve) => {
const results = Array.from(urls, () => null);
let numSuccess = 0;
let waitIdx = maxNum;
function triggerNext() {
if (waitIdx < urls.length) {
const thisIdx = waitIdx;
myFetch(urls[thisIdx]).then((r) => {
results[thisIdx] = r;
numSuccess++;
triggerNext();
});
waitIdx++;
}
if (numSuccess === urls.length) resolve(results);
}
for (let i = 0; i < maxNum; i++) {
myFetch(urls[i]).then((r) => {
results[i] = r;
numSuccess++;
triggerNext();
});
}
});
}
multiRequest([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13], 6).then((r) =>
console.log(r)
); |
function multiRequest(urls, maxNum) {
const dl = [];
const resArr = [];
let maxIndx = 0;
function getPromise(i, curIndex) {
maxIndx++;
return new Promise((resolve, reject) => {
console.log("开始" + curIndex, new Date().toLocaleString());
setTimeout(() => {
if (Math.random() < 0.5) {
resolve(curIndex);
} else {
reject(new Error(curIndex));
}
console.log("结束" + curIndex, new Date().toLocaleString());
}, 10000 * Math.random());
}).then(
(res) => {
return promiseHandler(res, i, curIndex);
},
(err) => {
return promiseHandler(err, i, curIndex);
},
);
}
function promiseHandler(res, i, curIndex) {
resArr[curIndex] = res;
if (maxIndx < urls.length) {
return getPromise(i, maxIndx);
}
return res;
}
for (let i = 0; i < maxNum; i++) {
dl.push(getPromise(i, i));
}
Promise.all(dl).then(() => {
console.log(resArr);
});
} |
比如maxNum设置为2时,第三个请求进来还是先执行了,是不是不对 |
|
|
|
this.taskList.push(resolve) 请问这里为什么添加resolve |
function ajax(url) {
return new Promise(resolve => {
setTimeout(() => {
resolve(url)
}, 10000 * Math.random())
})
}
function multiRequest(urls, maxNums) {
return new Promise(resolve => {
const list = [...urls]
const result = new Map()
const pending = new Set()
const request = (url) => {
pending.add(url)
ajax(url).then((data) => onFulfilled(url, data), err => onFulfilled(url, err))
}
const onFulfilled = (url, data) => {
result.set(url, data)
pending.delete(url)
if (!pending.size) {
return resolve([...result.values()])
} else if (list.length) {
request(list.shift())
}
}
for (let i = 0; i < urls.length; i++) {
const url = urls[i]
result.set(url, null)
if (i <= maxNums) {
request(list.shift())
}
}
})
}
const urls = [
'https://www.hello1',
'https://www.hello2',
'https://www.hello3',
'https://www.hello4',
'https://www.hello5',
'https://www.hello6',
'https://www.hello7',
'https://www.hello8',
'https://www.hello9',
'https://www.hello10',
'https://www.hello11',
'https://www.hello12'
]
multiRequest(urls, 3).then(res => {
console.log('sucssss======')
console.log(res)
}) |
function multiRequest(urls, maxNum) {
return new Promise((resolve) => {
const roundLength = Math.ceil(urls.length / maxNum)
const groupTasks = []
function executTaskGroup (tasks) {
return Promise.all(tasks.map((url) => {
return request(url)
}))
}
for(let i = 0; i < roundLength; i++) {
const tasks = urls.slice(i * maxNum, i * maxNum + maxNum)
groupTasks.push(executTaskGroup(tasks))
}
Promise.all(groupTasks).then((res) => {
resolve(res.flat())
})
})
}
function request(url) {
return new Promise(resolve => {
setTimeout(() => {
resolve(url)
}, 10000 * Math.random())
})
} 直接用分頁的概念做,最後用 flat 把結果壓平 |
|
function multiTask(proms, max) {
let sum = proms.length;
let count = 0;
let done = 0;
const res = [];
return new Promise((rs, rj) => {
while (count < max) {
next();
}
function next() {
const cur = Math.min(count++, sum);
if (cur >= sum) {
done === sum && rs(res);
return;
}
proms[cur].then(r => {
res[cur] = r;
}).catch(e => {
res[cur] = e;
}).finally(() => {
done++;
next();
})
}
});
}
function request(url) {
return new Promise((rs, rj) => {
setTimeout(() => rs(url), Math.random() * 1e3);
});
}
const proms = new Array(20).fill(1).map((_, n) => request(n));
multiTask(proms, 10).then(res => {
console.log(res)
}) |
控制异步请求的并行个数,内部通过 maxNum 和栈内异步请求是否为空,决定是否拉取最新的请求执行。 class Queue {
constructor() {
this.queues = [];
}
push(data) {
return this.queues.push(data);
}
shift() {
return this.queues.shift();
}
isEmpty() {
return this.queues.length === 0;
}
}
class TaskWrapper {
constructor(data) {
const rawData = data;
this.getRaw = () => rawData;
}
}
class TaskPool {
constructor(size) {
this.size = size;
this.queue = new Queue();
}
/**
* 添加任务到队列中
* @param fn
* @param args
* @returns Promise
*/
add(fn, args) {
return new Promise((resolve) => {
const task = new TaskWrapper({ resolve, fn, args });
this.queue.push(task);
// 根据 size 来初始并发执行指定数量的任务
// 当 size 为 0 时, 暂停任务的执行, 等待正在执行的异步任务完成后 size 自增
if (this.size) this.run();
});
}
/**
* taskFn 执行结果返回一个 Promise 对象实例
* @param taskFn Promise
* @param taskFnArgs any
* @return Promise
*/
runTask(taskFn, taskFnArgs) {
const taskFnResult = Promise.resolve(taskFn(taskFnArgs));
const end = () => {
this.size++;
this.pullTask();
};
taskFnResult.then(end, end);
return taskFnResult;
}
/**
* 分批次拉取队列中的任务执行
* 直到队列为空或者size为0
*/
pullTask() {
if (this.queue.isEmpty() || this.size === 0) return;
this.run();
}
/**
* 为调度运行真正的 task 前做准备
*/
run() {
this.size--;
const { taskResolve, taskFn, taskFnArgs } = this.getRaw();
taskResolve(this.runTask(taskFn, taskFnArgs));
}
/**
* 获取队头的任务
* @returns {{taskFn: *, taskResolve: *, taskFnArgs: *}}
*/
getRaw() {
const task = this.queue.shift();
const {
resolve: taskResolve,
fn: taskFn,
args: taskFnArgs,
} = task.getRaw();
return {
taskResolve,
taskFn,
taskFnArgs,
};
}
}
/**模拟Http请求的函数 */
const request = (url) => {
return new Promise((resolve) => {
console.log('发送 Http 请求');
// 请求 URL
setTimeout(() => {
resolve(url);
}, 2000);
});
};
/** 实现 */
function multiRequest(urls, maxNum) {
const taskPool = new TaskPool(maxNum);
return Promise.all(urls.map((url) => taskPool.add(request, url)));
}
/** 演示 */
multiRequest(['/api/user', '/api/banners', '/api/task'], 1).then((res) => {
console.log('res:', res);
}); 演示三个请求,依次执行的效果,maxNum 为 1 |
/**
* 带并发限制的批量请求函数
* @param {*} urls
* @param {*} max
* @param {*} callback
*/
function multiFetch(urls, max, callback) {
const urlCount = urls.length;
const urlsWithIndex = urls.map((url, index) => ({ url, index }));
const result = [];
// 批量发起请求
for (let i = 0; i < urlCount && i < max; i++) {
request(urlsWithIndex.shift());
}
function request({ url, index }) {
fetch(url).then(res => {
result[index] = res;
}).catch(err => {
result[index] = err;
}).finally(() => {
// 进行下一个请求
if (urlsWithIndex.length) request(urlsWithIndex.shift());
// 全部请求完成
if (result.length === urlCount) callback(result);
});
}
} |
这个 |
function req(url) {
return new Promise((resolve) => {
console.log('start: ' + url)
setTimeout(() => {
console.log('end: ' + url)
resolve('res' + url)
}, Math.random() * 1000)
})
}
const URLS = new Array(25).fill(0).map((_, i) => {
return i.toString()
})
async function multiRequest(urls, maxNum) {
const inRequest = [] // 正在请求的任务的promise数组
const results = [] // 所有任务的promise数组
const inReqIndxs = [] // 正在请求的任务的index的数组 (仅仅为了log验证请求的并发情况)
for (let i = 0; i < urls.length; i++) {
const url = urls[i]
let p
// 在下一个请求任务准备进来时,先看下是否正在请求任务数量达到maxNum
if (inRequest.length === maxNum) {
// 如果是,则等待任意进行中的任务完成
await Promise.any(inRequest)
}
// 推入下一个任务
p = req(url)
inRequest.push(p)
inReqIndxs.push(i)
results.push(p)
console.log(inReqIndxs)
// 每个任务本身则需要在自身完成时退出任务数组
p.then((val) => {
inRequest.splice(inRequest.indexOf(p), 1)
inReqIndxs.splice(inReqIndxs.indexOf(i), 1)
results[i] = val
})
}
return Promise.all(results)
}
multiRequest(URLS, 5).then(results => {
console.log('final results: ' + results)
})
/*
start: 0
[ 0 ]
start: 1
[ 0, 1 ]
start: 2
[ 0, 1, 2 ]
start: 3
[ 0, 1, 2, 3 ]
start: 4
[ 0, 1, 2, 3, 4 ]
end: 4
[ 0, 1, 2, 3 ]
start: 5
[ 0, 1, 2, 3, 5 ]
end: 2
[ 0, 1, 3, 5 ]
...
start: 22
[ 17, 19, 20, 21, 22 ]
end: 20
start: 23
[ 17, 19, 21, 22, 23 ]
end: 17
start: 24
[ 19, 21, 22, 23, 24 ]
end: 19
end: 23
end: 22
end: 21
end: 24
final results: res0,res1,res2,res3,res4,res5,res6,res7,res8,res9,res10,res11,res12,res13,res14,res15,res16,res17,res18,res19,res20,res21,res22,res23,res24
*/ |
|
最烦这种题目,弯弯绕绕的地方不少,但实际项目中屌用没用。题目其实有个障眼法:那个最大连接数,这个只有在第一次请求时才有效,后面多个请求是分别返回的,不存在并发的情况(JS 是单线程的,及时是多个请求同时返回也需要排队)。 async function multiRequest(urls, maxNum) {
const urlCount = urls.length;
const result = {};
let sendCount = 0;
let returnCount = 0;
const doRequest = (url) => {
return new Promise((resolve) => {
setTimeout(() => {
console.log(`request url: ${url}, success`);
resolve(`data of : ${url}`);
}, 1000);
});
};
return new Promise((resolve) => {
const fetchUrlData = (url) => {
sendCount++;
return doRequest(url).then((ret) => {
result[url] = ret;
returnCount++;
}).finally(() => {
if (sendCount < urlCount) {
fetchUrlData(urls[sendCount]);
} else {
if (returnCount === urlCount) {
resolve(result);
}
}
});
}
Promise.all(urls.slice(0, maxNum).map((url, idx) => {
return fetchUrlData(url);
}));
});
}
var urls = Array.from({ length: 20 }).map((i,idx) => idx + 1);
multiRequest(urls, 3).then((result) => {
console.log('result: ', result);
}); |
function ajax(url){
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(url);
}, Math.random() * 1000);
});
}
async function multiRequest(urls, maxNum) {
let promises = [];
let result = [];
let i = 0;
function l(id) {
return ajax(urls[id]).then(res => {
result[id] = res;
});
};
while(i < maxNum) {
promises.push(l(i));
i++;
}
while(i < urls.length) {
await Promise.race(promises).then(() => {
promises.push(l(i));
i++;
})
}
return Promise.all(promises).then(() => {
console.log(result);
return result;
});
}
multiRequest([1,2,3,4,5,6,7,8,9], 3); |
function multiRequest(urls, maxNum) {
return new Promise(resolve => {
console.time('total response time');
const startTime = performance.now();
const result = [];
let requestCount = 0;
let responseCount = 0;
for(let i = 0; i < Math.min(maxNum, urls.length); i++) {
fetchData(i);
}
async function fetchData(i) {
console.log('afer', performance.now() - startTime, 'ms, request ', urls[i]);
requestCount++;
try {
const data = await createPromise(i);
result[i] = data;
} catch(e) {
// TODO 处理错误
result[i] = e;
}
responseCount++;
console.log('afer', performance.now() - startTime, 'ms, response ', urls[i]);
if(requestCount < urls.length) {
fetchData(requestCount);
}
if(responseCount === urls.length) {
console.timeEnd('total response time')
resolve(result);
}
}
});
}
function createPromise(url) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if(url == 5) {
reject(new Error(url));
} else {
resolve(url);
}
}, (10 - url) * 1000 );
});
}
multiRequest(Array.from({length: 10}, (_, i) => i + 1), 5).then((data) => {
console.log(data);
}); |
async function multiRequest(urls, maxNum) {
const rets = []
let index = 0
let resolved
let size = urls.length
async function addTask(urls, maxNum) {
const url = urls.shift()
index++
function next(urls, index) {
if (urls.length) {
if (index < maxNum) {
addTask(urls, maxNum)
}
} else if (size === rets.length) {
resolved(rets)
}
}
fetch(url).then(res => {
rets.push(res)
index--
next(urls, index)
}).catch(res => {
rets.push(res)
index--
next(urls, index)
}).finally((res) => {
})
next(urls, index)
}
return new Promise((resolve, reject) => {
resolved = resolve
addTask(urls, maxNum)
})
}
let url2 = 'https://api.github.com/search/users?q=d';
let arr = new Array(10).fill(url2).map((v, i) => `${v}&i=${i}`)
multiRequest(arr, 2).then((res) => {
console.log(res)
}) |
|
要求如下:
The text was updated successfully, but these errors were encountered: