有没有办法将HTTP请求保留到待处理请求数小于N?

Is there a way to hold HTTP requests to be made until the number of pending requests are less than N?

提问人:Neeraj Kumar 提问时间:8/8/2022 更新时间:8/11/2022 访问量:102

问:

我正在研究用户可以一次上传 N 个文件的东西。但是,由于我还一次发送 N 个并行请求(每个文件上传 1 个请求),它很容易阻塞服务器,并且它会开始为发出的请求抛出 429(请求过多)错误。

如果已经有 K 个待处理的请求,有没有办法阻止发出请求(也不会阻止 UI)?

这里有一个最小的代码块,可以让你了解我目前正在做什么:

filesToUpload.forEach(function(file) {
    // abstract function which converts the given file to base64,
    // prepares the payload, makes a POST request and returns a promise
    upload(file, successCallback, errorCallback);
}
javascript angularjs ajax

评论

2赞 IT goldman 8/8/2022
但是您可以(并且应该)用于在一个请求中附加多个文件formData
0赞 CBroe 8/8/2022
无论如何,您都应该使用 FormData,而不是对数据进行 base64 编码 - 后者将使您需要传输的数据量增加约 4/3。
0赞 Neeraj Kumar 8/8/2022
@ITgoldman 就我而言,后端调用了第三方 API,这些 API 也被阻塞了。因此,这将导致非常高的响应时间。
0赞 Naren Murali 8/8/2022
@NeerajKumar修改 API 以接受多个文件!
0赞 Jaromanda X 8/8/2022
浏览器对每个服务器的并行请求数量有限制 - (尽管 HTTP/2 可能不是这种情况) - 无论如何,您实际上是收到 429 错误,还是纯粹是假设的情况?和。。。到底什么是“N”,极限是什么

答:

1赞 Oliveris 8/8/2022 #1

我认为最好的方法是在异步函数中发送HTTP请求。

async 和 await 关键字支持基于 promise 的异步行为

该关键字将等待您的发布请求完成,然后再向存储这些文件的服务器发送新的 HTTP 请求。await

您可以在此处找到有关 和 关键字及其语法的更多信息。asyncawait

评论

0赞 Jaromanda X 8/8/2022
这很好,只要你返回一个......没有理由认为问题中涉及任何承诺 - “上传”函数可以是任何东西,而且它绝对不会返回 Promise - 此外,如果您展示代码示例,这将是一个更好的答案awaitPromise
0赞 Neeraj Kumar 8/8/2022
不,这将使上传按顺序进行,我不想要这样
0赞 IT goldman 8/8/2022 #2

这个使用经典回调:允许最多 K 个并行上传,否则等待。该函数可以转换为 Promise。upload

var filesToUpload = [
  "file1.txt",
  "file2.txt",
  "file3.txt",
  "file4.txt",
  "file5.txt",
  "file6.txt",
  "file7.txt",
  "file8.txt",
]

function upload(filename, callback) {
  // abstract function which converts the given file to base64,
  // prepares the payload, makes a POST request and returns a promise
  setTimeout(function() {
    console.log("<!-- uploaded " + filename);
    typeof callback === 'function' && callback()
  }, Math.random() * 1000 + 500)
}


function start_sequence(filesToUpload, maxK, callback) {
  var K = maxK;

  function do_loop(filesToUpload, callback) {

    if (!filesToUpload.length) {
      if (K == maxK) {

        // end the loop
        typeof callback === 'function' && callback();
      }
      return;
    }

    // upload now first K's
    while (K > 0) {
      K--
      var first = filesToUpload.shift();
      console.log("--> sending " + first);
      upload(first, function() {
        K++;
        do_loop(filesToUpload, callback)
      });
    }
  }

  // start the loop
  do_loop(filesToUpload, callback);

}


start_sequence(filesToUpload, 3, function() {
  console.log("all done!")
});
.as-console-wrapper {
  max-height: 100% !important;
  top: 0;
}

0赞 Jrdo 8/8/2022 #3

您可以使用递归按顺序执行此操作。这需要修改以处理错误。

(function fn(files, successCallback, errorCallback){
    if(files.length){
        upload(files.shift(), (...args) => {
            successCallback(...args);
            fn(files, successCallback, errorCallback)
        }, errorCallback);
    }
})(filesToUpload, successCallback, errorCallback);

评论

0赞 Neeraj Kumar 8/8/2022
不,这将使上传按顺序进行,我不想要这样
0赞 Benz Stevox 8/8/2022 #4

使用循环和队列应该适合您的情况。

const UPLOAD_QUEUE = [];
let progress_queue = [];
const MAX_UPLOADS = 5;

throttledUpload();

function throttledUpload() {
    const max = MAX_UPLOADS - progress_queue.length;
    for(let i = 0; i < UPLOAD_QUEUE.length; i++){
        if(i > max) break;
        uploadFile(UPLOAD_QUEUE[i], i);
    }
    progress_queue = progress_queue.concat(UPLOAD_QUEUE.splice(0, max));
}

async function uploadFile(file, idx) {
    try {
        await upload(file, successCallback, errorCallback);
        throttledUpload();
    } catch (err) {
        UPLOAD_QUEUE.push(file);
    } finally {
        progress_queue.splice(idx, 1);
        throttledUpload();
    }
}