JavaScript变量赋值/返回

JavaScript变量赋值/返回,javascript,d3.js,Javascript,D3.js,我正在使用d3.js加载2个csv文件,并希望合并它们。然而,我被一些更基本的东西困住了 我有以下功能,工作正常: function loadData(file) { d3.csv(file, function (d){...}, function (data) {displayData(data);}); } 现在,我正试图以一种方式重构代码,即使用loadData()返回data对象,这样我可以调用它两次,合并data数组,并使用合并的数组调用displayData

我正在使用d3.js加载2个csv文件,并希望合并它们。然而,我被一些更基本的东西困住了

我有以下功能,工作正常:

function loadData(file) {
   d3.csv(file, function (d){...}, 
        function (data) {displayData(data);});
}
现在,我正试图以一种方式重构代码,即使用
loadData()
返回
data
对象,这样我可以调用它两次,合并
data
数组,并使用合并的数组调用
displayData()

我尝试返回
数据

function loadData(file) {
   d3.csv(file, function (d){...}, 
        function (data) {return data});

   return data;
}
使用全局变量

 var gdata1 = {};
 var gdata2 = {};
 function loadData(file) {
   d3.csv(file, function (d){...}, 
        function (data) {gdata = data});

   gdata2 =  data;
}
 var gdata1 = {};
 var gdata2 = {};
 function loadData(file) {
   d3.csv(file, function (d){...}, 
        function (data) {gdata = data; displayData(gdata)});


}
在许多其他事情中,似乎没有什么起作用

令人惊讶的是

使用全局变量

 var gdata1 = {};
 var gdata2 = {};
 function loadData(file) {
   d3.csv(file, function (d){...}, 
        function (data) {gdata = data});

   gdata2 =  data;
}
 var gdata1 = {};
 var gdata2 = {};
 function loadData(file) {
   d3.csv(file, function (d){...}, 
        function (data) {gdata = data; displayData(gdata)});


}
很好


谁能解释一下从displayData函数中取出数据数组的最佳/正确方法是什么,以及如何合并两个数据数组(我希望数据是一个映射数组,例如数据[0]是一个映射)。

承诺帮助您处理一些回调时有点不愉快的事情。你应该看看

这个小小的d3插件将使它工作:

因此基本上,
d3.promise.csv
取代了
loadData
函数

或者按照以下方式进行包装,以始终使用相同的格式化程序:

function loadData(file) {
    return d3.promise.csv(file, function (d){...});
}
编辑:

不幸的是,我不能使用任何插件,只有“核心”d3

然后你基本上可以把整个插件复制粘贴到你的代码中,其实没那么多;)

对于这种特殊情况,核心功能可以归结为:

function loadCsv(url){
    return new Promise(function(resolve, reject){
        d3.csv(url, function (d){...}, function(err, data){
            if(err) reject(Error(err));
            else resolve(data);
        });
    });
}

该插件几乎只是以同样的方式包装了更多的方法(如json、xml等),因此更具通用性。您应该看一下源代码。

承诺可以帮助您处理回调中有点不愉快的一些事情。你应该看看

这个小小的d3插件将使它工作:

因此基本上,
d3.promise.csv
取代了
loadData
函数

或者按照以下方式进行包装,以始终使用相同的格式化程序:

function loadData(file) {
    return d3.promise.csv(file, function (d){...});
}
编辑:

不幸的是,我不能使用任何插件,只有“核心”d3

然后你基本上可以把整个插件复制粘贴到你的代码中,其实没那么多;)

对于这种特殊情况,核心功能可以归结为:

function loadCsv(url){
    return new Promise(function(resolve, reject){
        d3.csv(url, function (d){...}, function(err, data){
            if(err) reject(Error(err));
            else resolve(data);
        });
    });
}
该插件几乎只是以同样的方式包装了更多的方法(如json、xml等),因此更具通用性。您应该看看源代码。

loadData()
应该进行回调。然后可以在第一个文件的回调中加载第二个文件

function loadData(file, callback) {
    d3.csv(file, function(d) { ...}, callback);
}

loadData(file1, function(err1, data1) {
    loadData(file2, function(err2, data2) {
        // code to combine data1 and data2 and display result
    });
});
这样做的缺点是它序列化了文件访问,因此它的性能不如在Thomas的回答中使用promission with
Promise.all()

要处理任意数量的文件,可以使用每次递增的变量从数组中提取它们

function loadNextFile(files, i, dataArray) {
    if (i >= files.length) {
        // merge dataArray and display it
    } else {
        loadData(files[i], function(err, data) {
            dataArray.push(data);
            loadNextFile(files, i+1, dataArray);
        }
    }
}
var filesToLoad = [...];
loadNextFile(filesToLoad, 0, []);
loadData()
应该进行回调。然后可以在第一个文件的回调中加载第二个文件

function loadData(file, callback) {
    d3.csv(file, function(d) { ...}, callback);
}

loadData(file1, function(err1, data1) {
    loadData(file2, function(err2, data2) {
        // code to combine data1 and data2 and display result
    });
});
这样做的缺点是它序列化了文件访问,因此它的性能不如在Thomas的回答中使用promission with
Promise.all()

要处理任意数量的文件,可以使用每次递增的变量从数组中提取它们

function loadNextFile(files, i, dataArray) {
    if (i >= files.length) {
        // merge dataArray and display it
    } else {
        loadData(files[i], function(err, data) {
            dataArray.push(data);
            loadNextFile(files, i+1, dataArray);
        }
    }
}
var filesToLoad = [...];
loadNextFile(filesToLoad, 0, []);

管理多个并发请求的状态,然后同步结果可能是一项相当艰巨的工作

管理状态是Promises的主要目的之一,而Promise.all是同步和合并结果

这也是下面代码的主要目的。还有两件事要说:

  • 此代码未经测试,可能包含一些错误

  • 我已经对这段代码中的几乎所有内容进行了注释,让您了解它的目的/机制是什么,它能做什么,以及如何处理这个怪物的不同用例。这就是为什么这个答案如此漫长的原因

由于加载单个文件的实际代码非常短且孤立,因此我决定将其放入一个外部函数中,这样您就可以通过只传递一个不同的实用程序函数来执行实际请求来重用整个代码

由于我更喜欢命名映射,而不是通过索引访问的普通数组(不混淆名称比索引更容易),所以我也集成了这种可能性。如果您不知道我的确切意思,请看一下主函数后面的示例

作为额外的补充,由于只需稍作调整,我将返回函数设置为递归函数,因此它可以处理作为URL“列表”传递给它的几乎所有内容

这段代码能处理什么

//plain urls, sure
loadCsvFiles('url', function(err, result){ ... })

//an array of urls, it's inital purpose
loadCsvFiles(['url1', 'url2', 'url3'], function(err, results){
    console.log(results[0], results[1], results[2]);
});

//urls mapped by property names, I've already mentioned that I prefer that over array indices
loadCsvFiles({
    foo: 'file1.csv',
    bar: 'file2.csv'
}, function(err, results){
    //where `results` resembles the structure of the passed mapping
    console.log(results.foo, results.bar);
})

//and through the recursive implementation, 
//pretty much every imaginable (non-circular) composition of the examples before
//that's where it gets really crazy/nice
loadCsvFiles({
    //mapping a key to a single url (and therefoere result)
    data: 'data.csv',

    //or one key to an array of results
    people: ['people1.csv', 'people2.csv'],

    //or a key to a sub-structure
    clients: {
        jim: 'clients/jim.csv',
        //no matter how many levels deep
        joe: {
            sr: 'clients/joe.sr.csv',
            jr: 'clients/joe.jr.csv',
        },
        //again arrays
        harry: [
            'clients/harry.part1.csv', 
            'clients/harry.part2.csv', 
            //and nested arrays are also possible
            [
                'clients/harry.part3a.csv',
                'clients/harry.part3b.csv'
            ]
        ]
    },

    //of course you can also add objects to Arrays
    images: [
        {
            thumbs: 'thumbs1.csv',
            full: 'full1.csv'
        },
        {
            thumbs: 'thumbs2.csv',
            full: 'full2.csv'
        }
    ]
}, function(err, results){
    //guess what you can access on the results object:
    console.log(
        results.data,
        results.people[0],
        results.people[1],
        results.clients.jim,
        results.clients.joe.sr,
        results.clients.joe.jr,
        results.clients.harry[0],
        results.clients.harry[1],
        results.clients.harry[2][0],
        results.clients.harry[2][1],
        results.images[0].thumbs,
        results.images[0].full,
        results.images[1].thumbs,
        results.images[1].full
    )
});
特别是最后一个例子可能对您没有任何意义,因为csv文件的结构很荒谬,但这不是重点。关键是,如何构建数据完全取决于您。只要把它传递给这个文件加载器,它就会处理这个问题


如果您希望同时支持多种文件格式,也可以通过简单的调整:

var loadDifferentFiles = loadFilesFactory(function(url, callback){
    if(!url || typeof url !== "string"){
        callback(JSON.stringify(url) + ' is no valid url');
        return;
    }

    if(url.endsWith('.csv')){
        return d3.csv(url, callback);
    }

    if(url.endsWith('.json')){
        return d3.json(url, callback);
    }

    //return d3.text(url, callback);
    callback('unsupported filetype: ' + JSON.stringify(url));
});
或者像这样的

var loadDifferentFiles = loadFilesFactory(function(value, callback){
    if(typeof value !== "string"){
        if(value.endsWith('.csv')){
            return d3.csv(value, callback);
        }

        if(value.endsWith('.json')){
            return d3.json(value, callback);
        }
    }

    //but in this case, if I don't know how to handle a value
    //instead of resolving to an error, just forwarding the passed value to the callback, 
    //implying that it probably wasn't meant for this code.
    callback(null, value);
});

管理多个并发请求的状态,然后同步结果可能是一项相当艰巨的工作

管理状态是Promises的主要目的之一,而Promise.all是同步和合并结果

这也是下面代码的主要目的。还有两件事要说:

  • 此代码未经测试,可能包含一些错误

  • 我已经对这段代码中的几乎所有内容进行了注释,让您了解它的目的/机制是什么,它能做什么,以及如何处理这个怪物的不同用例。这就是为什么这个答案如此漫长的原因

由于加载单个文件的实际代码非常短且孤立,因此我决定将其放入一个外部函数中,这样您就可以通过只传递一个不同的实用程序函数来执行实际请求来重用整个代码

因为我更喜欢命名映射而不是plai