[英]Sending thousands of fetch requests crashes the browser. Out of memory
我的任务是使用javascript和API将很大一部分数据从一个数据库传输到另一个数据库。 是的,我知道可以使用更好的方法来完成此任务,但是我被要求尝试这种方法。
我写了一些JavaScript,该API对返回一个数据数组的api进行GET调用,然后我转过身对另一个api进行调用,以将这些数据作为单独的POST请求发送。
到目前为止,我写的内容似乎运行良好,并且能够发送5万多个单独的POST请求,而没有任何错误。 但是当POST请求的数量增加到超过10万左右时,我遇到了麻烦。 我最终用完了内存,浏览器崩溃了。
据我到目前为止对promise的了解,可能存在一个问题,即promise(或其他东西)在解析后仍保留在堆内存中,这导致在请求过多后内存不足。
搜索过去几天后,我尝试了3种不同的方法来将所有记录成功地发布到POST。 这包括使用Bluebirds Promise.map,以及在将数组作为POST请求发送之前先将其分解为大块。 每种方法似乎都可以工作,直到崩溃前已处理了约10万条记录。
async function amGetRequest(controllerName) {
try{
const amURL = "http://localhost:8081/api/" + controllerName;
const amResponse = await fetch(amURL, {
"method": "GET",
});
return await amResponse.json();
} catch (err) {
closeModal()
console.error(err)
}
};
async function brmPostRequest(controllerName, body) {
const brmURL = urlBuilderBRM(controllerName);
const headers = headerBuilderBRM();
try {
await fetch(brmURL, {
"method": "POST",
"headers": headers,
"body": JSON.stringify(body)
});
}
catch(error) {
closeModal()
console.error(error);
};
};
//V1.0 Send one by one and resolve all promises at the end.
const amResult = await amGetRequest(controllerName); //(returns an array of ~245,000 records)
let promiseArray = [];
for (let i = 0; i < amResult.length; i++) {
promiseArray.push(await brmPostRequest(controllerName, amResult[i]));
};
const postResults = await Promise.all(promiseArray);
//V2.0 Use bluebirds Promise.map with concurrency set to 100
const amResult = await amGetRequest(controllerName); //(returns an array of ~245,000 records)
const postResults = Promise.map(amResult, async data => {
await brmPostRequest(controllerName, data);
return Promise.resolve();
}, {concurrency: 100});
//V3.0 Chunk array into max 1000 records and resolve 1000 promises before looping to the next 1000 records
const amResult = await amGetRequest(controllerName); //(returns an array of ~245,000 records)
const numPasses = Math.ceil(amResult.length / 1000);
for (let i=0; i <= numPasses; i++) {
let subset = amResult.splice(0,1000);
let promises = subset.map(async (record) => {
await brmPostRequest(controllerName, record);
});
await Promise.all(promises);
subset.length = 0; //clear out temp array before looping again
};
在解决这些诺言之后,将这些诺言从内存中清除,我是否缺少一些东西?
还是完成这项任务的更好方法?
编辑:免责声明-我对JS还是很陌生,仍然在学习。
“好吧……你需要节制这东西!”
没有(请原谅……)尝试不深入代码,“无论需要传输多少记录,您都需要控制浏览器一次尝试进行的请求数量。”
现在可能正在发生的事情是,您正在本地内存中堆积成百上千个“应许”请求-但是,浏览器一次可以实际传输多少个请求? 这应控制浏览器实际尝试执行的请求数。 在返回每个答复时,您的软件将决定是否启动另一个请求,以及是否为哪个记录。
从概念上讲,您有很多“工蜂”,这取决于您的浏览器可以同时执行的实际网络请求数。 您的软件永远不会尝试同时发起更多的并发请求:仅在完成每个请求后才启动一个新请求。 每个请求在完成后都会触发决定启动下一个请求的代码。
因此, 您永远不会 “发送数千个获取请求”。 即使以这种您控制的方式,“成千上万的请求最终都会被发送”,您一次可能只发送少数几个。
由于您不参与brmPostRequest()
传递的值,因此映射原始数组没有任何意义。 诺言和结果都不需要累积。
不这样做将节省内存, 并可能使进度超过100k固定点。
async function foo() {
const amResult = await amGetRequest(controllerName);
let counts = { 'successes': 0, 'errors': 0 };
for (let i = 0; i < amResult.length; i++) {
try {
await brmPostRequest(controllerName, amResult[i]);
counts.successes += 1;
} catch(err) {
counts.errors += 1;
}
};
const console.log(counts);
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.