首页 > 解决方案 > 发送数千个获取请求会使浏览器崩溃。记不清

问题描述

我的任务是使用 javascript 和 API 将大部分数据从一个数据库传输到另一个数据库。是的,我知道有更好的方法来完成这项任务,但我被要求尝试这种方法。

我编写了一些 javascript,对返回数据数组的 api 进行 GET 调用,然后我转身并调用另一个 api 以将此数据作为单独的 POST 请求发送。

到目前为止,我所写的内容似乎运行良好,并且我已经能够发送超过 50k 个单独的 POST 请求而没有任何错误。但是当 POST 请求的数量超过 100k 时,我遇到了麻烦。我最终内存不足,浏览器崩溃。

根据我目前对 Promise 的了解,可能存在一个问题,即 Promise(或其他东西?)在解决后仍保留在堆内存中,这导致在太多请求后内存不足。

在搜索过去几天后,我尝试了 3 种不同的方法来成功发布所有记录。这包括使用 Bluebirds Promise.map,以及在将数组作为 POST 请求发送之前先将数组分解成块。每种方法似乎都可以正常工作,直到它在崩溃之前处理了大约 100k 条记录。

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 还是很陌生,还在学习。

标签: javascriptpromisebluebirdfetch-api

解决方案


“好吧……你需要给这东西踩油门!”

无需(请原谅我......)试图深入研究您的代码,“无论您需要传输多少记录,您都需要控制浏览器在任何时候尝试执行的请求数量。”

现在可能发生的情况是,您在本地内存中堆积了成百上千个“承诺的”请求——但是,浏览器实际上一次可以传输多少个请求?这应该控制浏览器实际尝试执行的请求数量。在返回每个回复时,您的软件会决定是否开始另一个请求,如果是,则针对哪个记录。

从概念上讲,根据您的浏览器可以同时执行的实际网络请求的数量,您拥有如此多的“工蜂”。您的软件永远不会尝试同时启动更多的请求:它只是在每个请求完成时启动一个新请求。每个请求在完成后都会触发决定启动下一个请求的代码。

所以——你永远不会“发送数千个获取请求”。您可能一次只发送少数几个,尽管以这种由您控制的方式,“最终确实会发送数千个请求”。


推荐阅读