在绘制d3图表之前检查已完成多个异步功能

时间:2014-07-21 18:34:23

标签: javascript asynchronous d3.js

背景信息:我尝试多次查询CrisisNET API,以查找每天在一系列日期内提交的报告数量。

问题:在使用totals数组绘制d3图表之前,如何检查所有异步函数是否已完成?

代码:

var format = d3.time.format.iso,
    min = format.parse('2014-06-01'),
    max = format.parse('2014-07-01'),
    range = d3.time.day.utc.range(min, max);

totals = [];

for (var i = 0; i < range.length - 1; i++) {

    var request = (function(before, after) {

        var url = 'http://api.crisis.net/item?'
            + '&before=' + format(before)
            + '&after=' + format(after)
            + '&placeName=Syria'
            + '&limit=0'
            + '&apikey=[apikey]';

        d3.json(url, function(error, data) {

            var total =  {

                'date': after,
                'total': data.total

            };

            totals.push(total);
        });
    }(range[i + 1], range[i]));
}

提前感谢您的帮助。我是JavaScript的新手,我仍在尝试理解回调,异步函数等。

3 个答案:

答案 0 :(得分:1)

如果您知道自己已经完成了多少函数,并且每个函数都有回调函数,那么您可以使用包装函数来计算它们,并在它们全部执行后执行其他操作。像这样:

function asyncCounter(numCalls, callback){
    this.callback = callback;
    this.numCalls = numCalls;
    this.calls = 0;
};

asyncCounter.prototype.increment = function(){
    if(this.calls++ === this.numCalls){
        this.callback();
    }
};

然后创建一个asyncCounter对象,并在每次读取JSON时递增它:

var myAsyncCounter = new asyncCounter(numCallsExpected, callback);

...//code outside the JSON calls
d3.json(url, function(error, data) {
    ... //code you want to execute before incrementing the counter
    myAsyncCounter.increment();
}
{p> callback将在d3.json调用中的所有代码完成后执行。

答案 1 :(得分:0)

这是我到目前为止所提出的问题。它有效,但我确信必须有更好的方法!

var format = d3.time.format.iso,
    min = format.parse('2014-06-01'),
    max = format.parse('2014-07-01'),
    range = d3.time.day.utc.range(min, max);

totals = [];

for (var i = 0; i < range.length - 1; i++) {

    var request = (function(before, after) {

        var url = 'http://api.crisis.net/item?'
            + '&before=' + format(before)
            + '&after=' + format(after)
            + '&placeName=Syria'
            + '&limit=0'
            + '&apikey=[apikey]';

        d3.json(url, function(error, data) {

            var total =  {

                'date': after,
                'total': data.total

            };

            totals.push(total);

            ready();
        });
    }(range[i + 1], range[i]));
}

function ready() {

    if (totals.length === range.length - 1) {
        // Draw chart here
    }
}

答案 2 :(得分:0)

您可以为加载数据时创建回调,并为变量调用次数创建变量。然后,当调用次数等于数据集中必须包含的项目数时,您就可以进行可视化了:

var itemsLoaded = 0;
function dataLoaded() {
  itemsLoaded++;
  if (itemsLoaded === range.length - 1) {
    createVisualization();
  }
}

然后在你的d3.json回调中,你可以在项目被推入dataLoaded后致电totals

d3.json(url, function(error, data) {
  var total =  {
    'date': after,
    'total': data.total
  };
  totals.push(total);
  dataLoaded();
});

如果totals中元素的顺序对您非常重要,您可能希望显式使用索引,而不是.push将它们放入数组中:

d3.json(url, function(error, data) {
  var total =  {
    'date': after,
    'total': data.total
  };
  totals[i] = total;
  dataLoaded();
});

另外,在评估变量request的函数时要小心语法。您需要确保首先评估表达式,然后调用它。

而不是......

var request = (function(before, after) {/*...*/}(range[i+1], range[i]));

......看起来应该是这样的(看看parens):

var request = (function(before, after) {/*...*/})(range[i+1], range[i]);