繁体   English   中英

发送数千个提取请求会使浏览器崩溃。 记不清

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM