如何获得待定承诺的数量?

时间:2016-10-23 18:41:22

标签: javascript promise ecmascript-6

我正在尝试使用以下逻辑创建一个发送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);
};

我该如何做到这一点?

2 个答案:

答案 0 :(得分:1)

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

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

关于大约5个最大请求的部分:

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

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

    当您解决/拒绝其中一个请求时,您也可以使用此类检查代替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函数。

答案 1 :(得分: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对象的代码段,用于模拟超过5个待处理请求的延迟和产生错误的请求。所有请求都需要1秒才能获得响应,尽管最后一个请求将产生错误状态并将重试3次,因此其承诺仅在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; }