如何在时间受限的情况下处理多个api调用?

时间:2014-01-13 10:51:51

标签: javascript api loops google-analytics google-analytics-api

我必须在尽可能短的时间内进行多次API调用。由于我不得不填充各种条件数据集,因此需要进行多次调用。假设我有n指标,每个指标都会使用m个可能的过滤器进行过滤。我想得到我生成的n*m查询的results.totalsForAllResults。

虽然我最初遇到了很多打嗝,knowing about closures解决了许多关于发送异步API调用时出现问题的问题。我甚至能够以正确的顺序处理好结果。现在我遇到一个问题,即每秒API请求的最大数量对我来说是一个问题。 Google Core Reporting API v3每秒最多允许10个API请求,而且我已经超过了此限制。

以下是我尝试进行API调用并处理响应的方法。我对结构没什么自由:

function getMetrics() {
    initResultsArray();
    for (mI = 0; mI < metricsArray.length; mI++) {
        (function (mI) {    //Closure. Holds a local value of 'mI' within the scope
            for (fI = 0; fI < filtersArray.length; fI++) { 
                (function (fI) {   //Closure. Holds a local value of 'fI' within the scope
                    gapi.client.analytics.data.ga.get({
                        'ids': tableID,
                        'start-date': startDate,
                        'end-date': endDate,
                        'metrics': metricsArray[mI],
                        'filters': filtersArray[fI],
                        'samplingLevel': 'HIGHER_PRECISION',
                    }).execute(function putToVar(results) {   //this fn is defined inline to get access to fI
                        console.log(results.totalsForAllResults);
                        resultsArray[mI][fI] = parseInt(results.totalsForAllResults[metricsArray[mI]]);
                    });
                })(fI); //This ends the closure for fI
            }
        })(mI); //This ends the closure for mI
    }
}
//Print results to console, called when I believe all results have been populated.
function logResults() {
    console.log(resultsArray);
}

我需要能够找出我是否在最后一秒内发出了10个查询并等待发送剩余的查询,因为每当我每秒超过10个查询时,我会得到空对象作为我的API调用的响应它破坏了我将值检索到数组中的能力。如何才能做到这一点?我不知道如何使用wait(),并且有人说如果您使用wait(),浏览器会无响应,我不知道setTimeOut()如何应用于我的问题。

mIfI是指标和过滤器的全局迭代器,而metricsArrayfiltersArray是表示GA API预期方式的指标和过滤器的字符串数组,我只需要遍历它们就可以获得很多results.TotalsForAllResults。执行API调用和响应没有问题。我唯一的问题是超过每秒10个查询限制而没有得到进一步的回复。

1 个答案:

答案 0 :(得分:1)

您可以先创建一个需要调用的调用列表,然后再将它们设置为10来解决此问题。这完全不在袖口,所以不能保证它真的有用,但希望你可以将它应用到你的情况。

一般的想法是创建一个简单的Scheduler构造函数,它接受一系列要处理的东西。更具描述性地命名内容会更好:)。创建的对象只有一个函数 - start

var Scheduler = function (stuffToProcess) {
    var started,
        executeFunction;

    getExecuteFunction = function (current) {
        return function (results) {
            console.log(results.totalsForAllResults);
            resultsArray[current.mI][current.fI] = parseInt(results.totalsForAllResults[metricsArray[current.mI]], 10);
        };
    }

    var processNext = function () {
        var current = stuffToProcess.shift(),
            counter = 0;

        while (current && ++counter <= 10) {
            gapi.client.analytics.data.ga
                .get(current.gaBit)
                .execute(getExecuteFunction(current));
            if (counter !== 10) {
                current = stuffToProcess.shift(); // <- EDIT: Forgot this in original answer.
            }
        }
        if (stuffToProcess.length > 0) {
            window.setTimeout(function () {
                processNext();
            }, 1000);
        }
    };

    this.start = function () {
        if (!started) {
            started = true;
            processNext();
        }
    };    
};

然后在你的getMetrics函数中,不是直接调用ga,而是构建一个你想要调用的数组,然后创建一个调度程序实例并启动它。

function getMetrics() {
    initResultsArray();
    var listOfCalls = [];
    for (mI = 0; mI < metricsArray.length; mI++) {
        for (fI = 0; fI < filtersArray.length; fI++) { 
            listOfCalls.push({
                gaBit: {
                    'ids': tableID,
                    'start-date': startDate,
                    'end-date': endDate,
                    'metrics': metricsArray[mI],
                    'filters': filtersArray[fI],
                    'samplingLevel': 'HIGHER_PRECISION'
                },
                mI: mI,
                fI: fI
            });
        }
    }
    var s = new Scheduler(listOfCalls);
    s.start();
}

编辑: 修改后的代码改为使用getExecuteFunction