【问题标题】:How to get the number of pending promises? [closed]如何获得未决承诺的数量? [关闭]
【发布时间】:2017-03-05 12:41:10
【问题描述】:

我正在尝试创建一个使用以下逻辑发送 HTTP 请求的函数:

  • 如果有 5 个或更多正在进行的请求,则必须推迟下一个请求,直到其中一个请求完成。只有这样才能处理下一个请求。
  • 当响应码不是200时,我想重试请求最多3次。如果重试3次后的响应码仍然不是200,则应该执行错误函数。

我如何知道正在进行的请求数量?我怎样才能等到其中一个完成后才能处理下一个请求?

这是我到目前为止所尝试的。 count 变量是用来统计失败次数的,但是我不知道如何统计挂起的 HTTP 请求:

var count = 0;

function get(url) {
    return new Promise(function(resolve, reject) {
        var xhttp = new XMLHttpRequest();
        xhttp.open("GET", url, true);
        xhttp.onload = function() {
            if (xhttp.status == 200) {
                resolve(JSON.parse(xhttp.response));
            } else if (xhttp.status != 200 && count != 3) {
                count++;
                resolve(JSON.parse(xhttp.response));
            } else if {
                reject(xhttp.statueText):
            } else(count == 3) {
                reject("you have reached the maximum number of tries")
            }
        }    
        xhttp.onerror = function() {
            reject(xhttp.statueText);
        };
        xhttp.send()
    });
}
var promise = get("data/tweets.json);
promise.then(function(tweets) {
    console.log(tweets);
};

我怎样才能做到这一点?

【问题讨论】:

  • 您有问题吗? Q+A 网站有点必要
  • 是的,我的问题是如何在 ES6 中设置待处理的请求!
  • 缩进代码发生了什么?
  • 您好@AmrAyoub,首先,请缩进您的代码以帮助其他人阅读,其次,您的问题似乎实际上至少有2个问题:a)如何重试失败的HTTP请求,b)如何将传出请求的数量限制为 5。无论如何,您究竟为什么要限制传出请求的数量?
  • @hi Jakub ,对不起,我不知道缩进代码是什么意思 :(

标签: javascript promise ecmascript-6


【解决方案1】:

这是一种不需要间隔计时器的方法。它使用一个函数队列在下一个 HTTP 响应到达时调用(这使得挂起的请求数低于 5):

var pending = 0;
var queue = [];

function get(url){
    return new Promise (function(resolve,reject){
        function loop(retries = 3) {
            var xhttp = new XMLHttpRequest();
            xhttp.open("GET",url,true);
            xhttp.onload = function(){
                pending--;
                if (xhttp.status == 200){
                    resolve(JSON.parse(xhttp.response));
                }
                else if (retries > 0) {
                    // retry by automatically relaunching the request:
                    loop(retries-1);
                } else {
                    // give up
                    reject(xhttp.statusText); // correct your spelling!
                }
                if (pending < 5 && queue.length) {
                    // extract and execute the first of the queued actions:
                    queue.shift()();
                }
            };
            xhttp.onerror= function(){
                reject(xhttp.statusText); // correct your spelling
            };
            xhttp.send()
            pending++;
        }
        if (pending >= 5) {
            // Push the call we want to make for later execution in a queue:
            queue.push(loop);
        } else {
            loop(); // try at the most 3 times
        }
    });
}

这是一个带有假 HTTPRequest 对象的 sn-p,用于模拟超过 5 个未决请求时的延迟和产生错误的请求。所有请求都需要 1 秒才能得到响应,尽管最后一个会产生错误状态并重试 3 次,因此它的 promise 仅在 3 秒后才解析:

// Overwrite the real XMLHttpRequest with a dummy one, just for this snippet (skip this in your code!):
function XMLHttpRequest() {
    this.open = function(_, url) {
        this.status = url.indexOf('fail') > -1 ? 201 : 200;
        this.response = JSON.stringify({text: 'Response from ' + url});
        this.statusText = this.status == 200 ? 'OK' : 'Error status from ' + url;
    };
    this.send = function () {
        setTimeout(this.onload.bind(this), 1000);
    }.bind(this);
}

var pending = 0;
var queue = [];

function get(url){
    return new Promise (function(resolve,reject){
        function loop(retries = 3) {
            var xhttp = new XMLHttpRequest();
            xhttp.open("GET",url,true);
            xhttp.onload = function(){
                pending--;
                if (xhttp.status == 200){
                    resolve(JSON.parse(xhttp.response));
                }
                else if (retries > 0) {
                    // retry by automatically relaunching the request:
                    loop(retries-1);
                } else {
                    // give up
                    reject(xhttp.statusText); // correct your spelling!
                }
                if (pending < 5 && queue.length) {
                    // extract and execute the first of the queued actions:
                    queue.shift()();
                }
            };
            xhttp.onerror= function(){
                reject(xhttp.statusText); // correct your spelling
            };
            xhttp.send()
            pending++;
        }
        if (pending >= 5) {
            // Push the call we want to make for later execution in a queue:
            queue.push(loop);
        } else {
            loop(); // try at the most 3 times
        }
    });
}

// Example series of calls to illustrate the effect of more than 5 simultanious requests
// and the 3 retries for an error producing request:
console.log('start');
get('example.com?1').then( function(obj) { console.log(obj.text) });
get('example.com?2').then( function(obj) { console.log(obj.text) });
get('example.com?3').then( function(obj) { console.log(obj.text) });
get('example.com?4').then( function(obj) { console.log(obj.text) });
get('example.com?5').then( function(obj) { console.log(obj.text) });
get('example.com?6').then( function(obj) { console.log(obj.text) });
get('example.com?7').then( function(obj) { console.log(obj.text) });
get('example.com?fail').catch( function(msg) { console.log(msg) });
.as-console-wrapper { max-height: 100% !important; top: 0; }

【讨论】:

  • 谢谢,这样容易多了。
【解决方案2】:

你的问题有点不清楚,但让我试一试。

您无法获得待处理的承诺,您需要将它们(或其中的一些)存储在某个地方。

关于最多 5 个请求的部分:

您要做的是限制(即速率限制)请求。 这是一个有点高级的话题,一个天真的实现可以做这样的解决方案:

  1. 每次创建请求时,都会增加一些变量,并在完成(成功与否)时减少它 (finally)
  2. 如果有超过限制的请求,则将其放入队列中以供稍后处理。
  3. 定期 (setInterval) 检查队列的大小,如果它非空且传出请求少于 5 个,则从队列中取出一个项目并执行 HTTP 请求

代替setInterval,您也可以在解决/拒绝其中一个请求时使用这种检查 - 然后使用setTimeout 安排检查(因为检查最近是否没有发生任何事情没有意义)。

var numberOfPendingRequests = 0;
var overLimitQueue = [];

function get(url) {
    var getPromise = new Promise(function(resolve, reject) {
        if (numberOfPendingRequests >= 5) {
            overLimitQueue.push({
                url: url,
                resolve: resolve,
                reject: reject
            });
            return;
        } else{
            numberOfPendingRequests++;
            ...
        }

    });
    getPromise.finally(function(){
        numberOfPendingRequests--;
    })
    return getPromise;
}

setInterval(function(){
    if (numberOfPendingRequests  < 5 && overLimitQueue.length > 0) {
        // process entries in the queue
    }
}, 1000);

无论如何,这种代码是相当先进的,你最好使用别人的库而不是自己做。

例如看看这个库:

https://github.com/remy/promise-throttle

可以将执行请求的部分代码提取到较小的辅助函数中,在用于重做调用或处理队列元素的代码中,您可以使用这些辅助方法。您可能需要在辅助方法调用之间传递 resolvereject 函数。

【讨论】:

  • 非常感谢,这正是我想要的:)
猜你喜欢
  • 2020-01-18
  • 2018-10-16
  • 2023-03-24
  • 2022-12-17
  • 1970-01-01
  • 2022-08-14
  • 1970-01-01
  • 1970-01-01
  • 2021-11-08
相关资源
最近更新 更多