请实现一个函数,可以批量请求数据,所有的url地址在urls参数中,同时可以通过max参数控制请求的并发量,当所有的请求结束后,需要执行callback回调函数,发请求的函数直接使用fetch即可。 函数的名称和参数如下: function sendRequest(urls, max, callback) {}
ChatGPT:
以下是一个使用async/await和Promise实现的sendRequest函数,可以满足您的需求:
async function sendRequest(urls, max, callback) {
// 去除重复的url
const uniqueUrls = [...new Set(urls)];
// 定义一个用于存储请求任务的数组
const tasks = [];
// 定义一个执行请求的内部函数
async function fetchUrl(url) {
try {
const response = await fetch(url);
const data = await response.json();
return data;
} catch (error) {
console.error(`Error fetching ${url}:`, error);
return null;
}
}
// 遍历所有的url,创建异步请求任务
for (const url of uniqueUrls) {
tasks.push(fetchUrl(url));
}
// 使用 Promise.all 控制并发数量
async function processBatch() {
while (tasks.length) {
const batch = tasks.splice(0, max);
await Promise.allSettled(batch);
}
}
// 处理所有请求任务
await processBatch();
// 当所有请求任务结束后,执行回调函数
if (typeof callback === 'function') {
callback();
}
}
这个函数首先通过Set去除了重复的url,然后遍历所有的url创建异步请求任务。我们使用Promise.allSettled对任务进行分批处理,控制并发数量。最后,当所有请求任务结束后,执行回调函数。
您可以这样使用这个函数:
const urls = [
'https://jsonplaceholder.typicode.com/todos/1',
'https://jsonplaceholder.typicode.com/todos/2',
'https://jsonplaceholder.typicode.com/todos/3',
];
sendRequest(urls, 2, () => {
console.log('所有请求已完成');
});
请注意,fetch函数在浏览器环境中是可用的,但在Node.js环境中需要使用额外的库(如node-fetch)。
上面的代码有没有错误?
ChatGPT:
在这段代码中,有一个小问题:在 processBatch
函数中,使用了 Promise.allSettled
方法来控制并发数量。但是,Promise.allSettled
并没有限制并发数量的功能,它只是等待所有 Promise 都 settled(即变成 fulfilled 或 rejected 状态)后才返回结果。因此,在这里应该改用 Promise.all
方法来控制并发数量。
修改后的代码:
async function processBatch() {
while (tasks.length) {
const batch = tasks.splice(0, max);
await Promise.all(batch);
}
}
这样就可以确保同时最多只有 max
个请求在进行了。
使用Promise.all会有个问题,比如最大并发是5,当有一个url执行完成后,如果使用的是Promise.all,那么就会有一个空位,直到其他4个执行完成
ChatGPT:
您说得没错,如果使用的是 Promise.all
,当有一个请求任务完成后,由于其他请求还在进行中,因此会导致整个批处理任务无法结束。为了解决这个问题,可以使用一个计数器来记录已经完成的任务数量,当计数器达到了 URL 数组的长度时,说明所有请求都已经完成。
修改后的代码:
async function processBatch() {
let i = 0;
while (i < uniqueUrls.length) {
const batch = tasks.splice(0, max);
await Promise.all(batch.map(task => task.then(() => i++)));
}
}
这样就能够确保所有请求都被执行完毕,并且也能够控制最大并发数量。
我的意思是,当有一个url执行完成后,如何再往里面及时补充,而不是让它空缺。
ChatGPT:
好的,如果您希望在一个请求完成后立即启动下一个请求,而不让队列中有空缺,则可以将 handleUrl
函数中的 processQueue()
改为 handleNextUrl()
。这样,每当处理完一个 URL 后,会立即调用 handleNextUrl()
函数来启动下一个请求。
修改后的代码:
async function sendRequest(urls, max, callback) {
const uniqueUrls = [...new Set(urls)];
const results = [];
let running = 0;
let i = 0;
async function handleUrl(url) {
running++;
try {
const response = await fetch(url);
const data = await response.json();
console.log("⭐data==>", data.id);
results.push(data);
} catch (error) {
console.error(`Error fetching ${url}:`, error);
results.push(null);
}
running--;
handleNextUrl();
}
function handleNextUrl() {
while (i < uniqueUrls.length && running < max) {
handleUrl(uniqueUrls[i++]);
}
if (running === 0 && i === uniqueUrls.length && typeof callback === "function") {
callback(results);
}
}
handleNextUrl();
}
这样就能够保证队列中没有空闲位置,并且能够及时添加新的请求。
请把上面的请求模拟成3s的延时
ChatGPT:
好的,为了模拟异步请求,我们可以使用 setTimeout
来模拟一个 3 秒的延迟。修改后的代码如下:
async function sendRequest(urls, max, callback) {
const uniqueUrls = [...new Set(urls)];
const results = [];
let running = 0;
let i = 0;
async function handleUrl(url) {
running++;
try {
// 模拟异步请求
await new Promise(resolve => setTimeout(resolve, 3000));
console.log(`Fetching ${url}...`);
const data = { url };
results.push(data);
} catch (error) {
console.error(`Error fetching ${url}:`, error);
results.push(null);
}
running--;
handleNextUrl();
}
function handleNextUrl() {
while (i < uniqueUrls.length && running < max) {
handleUrl(uniqueUrls[i++]);
}
if (running === 0 && i === uniqueUrls.length && typeof callback === "function") {
callback(results);
}
}
handleNextUrl();
}
在这个版本的代码中,我们使用了 await new Promise(resolve => setTimeout(resolve, 3000))
来模拟异步请求。这会使每个请求都有 3 秒的延迟,并且在完成时会将一个包含 URL 的数据添加到 results
数组中。
最后经过测试,确实完美执行。
完整代码如下:
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1) + min);
}
async function sendRequest(urls, max, callback) {
const uniqueUrls = [...new Set(urls)];
const results = [];
let running = 0;
let i = 0;
async function handleUrl(url) {
running++;
console.log("⭐running==>", running);
try {
// 模拟异步请求
await new Promise((resolve) =>
setTimeout(resolve, getRandomInt(100, 5000))
);
console.log(`Fetching ${url}...`);
const data = { url };
results.push(data);
} catch (error) {
console.error(`Error fetching ${url}:`, error);
results.push(null);
}
running--;
handleNextUrl();
}
function handleNextUrl() {
while (i < uniqueUrls.length && running < max) {
handleUrl(uniqueUrls[i++]);
}
if (
running === 0 &&
i === uniqueUrls.length &&
typeof callback === "function"
) {
callback(results);
}
}
handleNextUrl();
}
const urls = [
"https://jsonplaceholder.typicode.com/todos/1",
"https://jsonplaceholder.typicode.com/todos/2",
"https://jsonplaceholder.typicode.com/todos/3",
"https://jsonplaceholder.typicode.com/todos/4",
"https://jsonplaceholder.typicode.com/todos/5",
"https://jsonplaceholder.typicode.com/todos/6",
"https://jsonplaceholder.typicode.com/todos/7",
"https://jsonplaceholder.typicode.com/todos/8",
"https://jsonplaceholder.typicode.com/todos/9",
"https://jsonplaceholder.typicode.com/todos/10"
];
sendRequest(urls, 3, () => {
console.log("所有请求已完成");
});