在我的React应用程序中,我有一个参数数组(例如一些ID),它们应该用作ajax调用队列的参数。问题是数组可能包含超过1000个项目,如果我只是使用forEach循环递归地进行ajax调用,浏览器页面最终会在每个请求被解析之前停止响应。
是否有技术/库,可以允许发送ajax请求,例如,每次异步发送5个请求,只有当这些请求完成后,继续下一个5?
跟进问题:
答案 0 :(得分:3)
好的,让我们解决一些问题。在JavaScript中,AJAX请求本质上是异步的。您选择在实现中使它们同步一些。
您需要做的是拥有一些请求数组,您可以从中一次弹出结果X,等待它们返回,然后重复。
{{1}}
将同时执行5个请求,等待它们完成,然后获取ID的下一部分
答案 1 :(得分:2)
我在项目中遇到了同样的问题。您需要的是一个优先级队列,以便控制将同时执行多少请求。我正在使用this library。由于p-queue实现很容易理解并且不是那么大,我已经将代码粘贴在下面的代码段中,只是为了向您展示它在最新行中的工作原理。
// IMPLEMENTATION ####################
// Port of lower_bound from http://en.cppreference.com/w/cpp/algorithm/lower_bound
// Used to compute insertion index to keep queue sorted after insertion
function lowerBound(array, value, comp) {
let first = 0;
let count = array.length;
while (count > 0) {
const step = (count / 2) | 0;
let it = first + step;
if (comp(array[it], value) <= 0) {
first = ++it;
count -= step + 1;
} else {
count = step;
}
}
return first;
}
class PriorityQueue {
constructor() {
this._queue = [];
}
enqueue(run, opts) {
opts = Object.assign({
priority: 0
}, opts);
const element = {
priority: opts.priority,
run
};
if (this.size && this._queue[this.size - 1].priority >= opts.priority) {
this._queue.push(element);
return;
}
const index = lowerBound(this._queue, element, (a, b) => b.priority - a.priority);
this._queue.splice(index, 0, element);
}
dequeue() {
return this._queue.shift().run;
}
get size() {
return this._queue.length;
}
}
class PQueue {
constructor(opts) {
opts = Object.assign({
concurrency: Infinity,
queueClass: PriorityQueue
}, opts);
if (opts.concurrency < 1) {
throw new TypeError('Expected `concurrency` to be a number from 1 and up');
}
this.queue = new opts.queueClass(); // eslint-disable-line new-cap
this._queueClass = opts.queueClass;
this._pendingCount = 0;
this._concurrency = opts.concurrency;
this._resolveEmpty = () => {};
this._resolveIdle = () => {};
}
_next() {
this._pendingCount--;
if (this.queue.size > 0) {
this.queue.dequeue()();
} else {
this._resolveEmpty();
if (this._pendingCount === 0) {
this._resolveIdle();
}
}
}
add(fn, opts) {
return new Promise((resolve, reject) => {
const run = () => {
this._pendingCount++;
fn().then(
val => {
resolve(val);
this._next();
},
err => {
reject(err);
this._next();
}
);
};
if (this._pendingCount < this._concurrency) {
run();
} else {
this.queue.enqueue(run, opts);
}
});
}
addAll(fns, opts) {
return Promise.all(fns.map(fn => this.add(fn, opts)));
}
clear() {
this.queue = new this._queueClass(); // eslint-disable-line new-cap
}
onEmpty() {
// Instantly resolve if the queue is empty
if (this.queue.size === 0) {
return Promise.resolve();
}
return new Promise(resolve => {
const existingResolve = this._resolveEmpty;
this._resolveEmpty = () => {
existingResolve();
resolve();
};
});
}
onIdle() {
// Instantly resolve if none pending
if (this._pendingCount === 0) {
return Promise.resolve();
}
return new Promise(resolve => {
const existingResolve = this._resolveIdle;
this._resolveIdle = () => {
existingResolve();
resolve();
};
});
}
get size() {
return this.queue.size;
}
get pending() {
return this._pendingCount;
}
}
// TEST ####################
const promises = new PQueue({
concurrency: 4
});
const makePromise = (key, time) => {
let response = null;
return new Promise(resolve => {
setTimeout(() => {
response = `Promise ${key} resolved`;
console.log(response);
resolve(response);
}, time);
});
}
promises.add(() => makePromise('p1', 5000));
promises.add(() => makePromise('p2', 1000));
promises.add(() => makePromise('p3', 3000));
promises.add(() => makePromise('p4', 6000));
promises.add(() => makePromise('p5', 2000));
promises.add(() => makePromise('p6', 1500));
promises.add(() => makePromise('p7', 5500));
promises.add(() => makePromise('p8', 7000));
promises.onIdle().then(() => {
console.log('Promises queue empty.');
});
答案 2 :(得分:2)
如果您不受es版本限制并且可以使用es6,那么您应该查看async await
async function makeBatchCalls(arrayIds, length) {
let test = arrayIds.reduce((rows, key, index) => (index % length == 0 ? rows.push([key])
: rows[rows.length-1].push(key)) && rows, []);
let Batchresults = [];
//convert them to two dimensionl arrays of given length [[1,2,3,4,5], [6,7,8,9,10]]
for (calls of test) {
Batchresults.push(await Promise.all(calls.map((call)=>fetch(`https://jsonplaceholder.typicode.com/posts/${call}`))));
}
return Promise.all(Batchresults); //wait for all batch calls to finish
}
makeBatchCalls([1,2,3,4,5,6,7,8,9,10,12,12,13,14,15,16,17,18,19,20],3)
async / await用于等待异步调用的确切场景。基本上在异步函数内部,直到await被解决,执行被暂停。在开始使用它们之前,您需要了解承诺和生成器。
答案 3 :(得分:0)