人力検索はてな
モバイル版を表示しています。PC版はこちら
i-mobile

javascriptでサーバーにあるCSVファイルをD3.jsで読み込んでいるのですが、読み込むタイミングが悪いのか、時々うまくデータを読みこまないことがあります。その場合、少し待ってからブラウザのreloadを何度か押すとうまく読み込んでくれるのですが、サーバーからデータを確実に読み込んでから次の処理に進ませるにはどうしたらいいでしょうか?

>||
//CSVファイルからクイズのタイトルを読み取る
var dataset0 = [];
d3.csv([parafilename], function(data) {
//CSVファイルのタイトルの一行目の文字だけを選択する。
title0 = data.map(function(d) { return d[para]; });
title = title0[0];
console.log(dataset0);
});

//初期設定へ進む
||<

●質問者: javelover
●カテゴリ:ウェブ制作
○ 状態 :終了
└ 回答数 : 1/1件

▽最新の回答へ

1 ● a-kuma3
ベストアンサー

こんな感じのコードを動かしてみると、理由が分かると思います。

d3.csv("foo.csv", function(data) {
 console.log("(1)");
 console.log(data);
});
console.log("(2)");

出力は、

(1)
Array [ Object, ...]
(2)

ではなく、通常は、こうなります。

(2)
(1)
Array [ Object, ...]

d3.js では csv を取ってくるのに非同期で処理をします。
データ取得のメソッドを呼ぶと、データの到着を待たずにメソッドからすぐに戻ってきます。
渡したコールバック関数は、データを取得したときに呼び出されます。

# d3.csv(url, accessor], callback])

Issues an HTTP GET request for the comma-separated values (CSV) file at the specified url. The file contents are assumed to be RFC4180-compliant. The mime type of the request will be "text/csv". The request is processed asynchronously, such that this method returns immediately after opening the request. When the CSV data is available, the specified callback will be invoked with the parsed rows as the argument.

CSV ? mbostock/d3 Wiki ? GitHub

なので、d3.csv の後に書いたコードの方が先に実行され、d3.csv に渡したコールバックの方が後で実行される、というふうになります。

一番、単純な対応はコールバック関数の中に、パラメータを使った初期設定の処理を記述することです。

//CSVファイルからクイズのタイトルを読み取る 
var dataset0 = [];
d3.csv([parafilename], function(data) {
 //CSVファイルのタイトルの一行目の文字だけを選択する。
 title0 = data.map(function(d) { return d[para]; });
 title = title0[0];
 console.log(dataset0);

 //★ 初期設定は、ここでやる
});

javeloverさんのコメント
a-kuma3さんありがとうございます。 コールバック関数の中に初期設定のコードをいくつか入れてみたら良さそうでした。 これはコールバック関数の中にコードを入れた場合、そのコードが先に実行されて 次にCSVのデータを取ってくるという順番になるのでしょうか? つまり、コールバック関数の中に入れるコードはあくまで、時間稼ぎ?あるいは処理タイミングをずらしてやる意味合いでしょうか? もし、d3.csv の後に書いたコードの方が先に実行されるとしたら、d3.csvでサーバーからデータを取ってくるまでの間、どこまで命令は先に進んでしまうか決まりはあるのでしょうか? あと、一連の流れで、全体のスクリプトの一番先頭にd3.jsのコードを書いてスクリプトの最初のほうでデータをサーバーから取ってくるようにした場合、コールバック関数の中に入れる初期設定のコードは1行程度でいいのか、あるいは初期設定のコードが何行かに渡っている場合、それらを全部コールバック関数に中に入れたほうがいいのか、どのあたりはどうしたらいいでしょうか?

a-kuma3さんのコメント
>> これはコールバック関数の中にコードを入れた場合、そのコードが先に実行されて 次にCSVのデータを取ってくるという順番になるのでしょうか? << 違います。 「コールバック (call back)」とは、その名の通り、呼び返されるものです。 d3.csv は、データを取ってきた後にコールバック関数を呼び出します。 無名関数でコールバック関数を作ると、ちょっと分かりづらいのかもしれません。 普通に function 文でコールバック関数を用意すると、こんな感じになります。 >|javascript| // コールバック関数の定義(実行じゃない) function my_callback_function(data) { //CSVファイルのタイトルの一行目の文字だけを選択する。 title0 = data.map(function(d) { return d[para]; }); ... } //CSVファイルからクイズのタイトルを読み取る var dataset0 = []; // d3.csv の呼び出し。 // パラメータとして関数を渡すと、d3.csv が呼び出してくれる。 d3.csv([parafilename], my_callback_function); ||< >> あと、一連の流れで、全体のスクリプトの一番先頭にd3.jsのコードを書いてスクリプトの最初のほうでデータをサーバーから取ってくるようにした場合、コールバック関数の中に入れる初期設定のコードは1行程度でいいのか、あるいは初期設定のコードが何行かに渡っている場合、それらを全部コールバック関数に中に入れたほうがいいのか、どのあたりはどうしたらいいでしょうか? << d3.csv で取ってくるデータを使う処理は、全てコールバック関数の中から呼び出します。 先に書いた通り、データを取得した後にコールバック関数が呼ばれるのですから。 とはいえ、コールバック関数が長くなるとソースが見づらいなあ、ということはあると思います。 処理の単位で関数化しておくのが良いのではないかと思います。 >|javascript| // ページ全体の読み込みが終わってから javascript を実行する(jQuery の仕組み) $(function() { d3.csv([parafilename], function(data) { // CSV のデータで画面の初期化をする処理を呼び出す initialize_question(data); }); // CSV のデータが取れてなくてもできる処理があれば、これ以降に書く ... }); // CSV のデータを使って、画面の初期化をする処理 function initialize_question(data) { ... } ||<

javeloverさんのコメント
a-kuma3さん いろいろ教えてくださりありがとうございます。 d3.csvが複数あるので、それぞれに対応したコードをわざわざ、それぞれ分けて関数化して、 1つ1つコールバック関数のところに入れるべきか悩みましたが 複数あるD3.csvコードの一番最後のコードのコールバック関数にd3.csvで呼び出したデータを使って画面表示させるところだけを関数化して、d3.csvのコールバック関数に入れてみました。 すると問題なく動きました。何度か試してみましたが今のところはデータはうまくロードできているようです。これを次に進めそうです。ありがとうございました。

a-kuma3さんのコメント
>> 複数あるD3.csvコードの一番最後のコードのコールバック関数にd3.csvで呼び出したデータを使って画面表示させるところだけを関数化して、d3.csvのコールバック関数に入れてみました。 << これ、たまたまのような気がします。 全部揃ったら実行する、というふうに書いておいた方が確実です。 >|javascript| var data1, data2, data3; // ひとつ目 d3.csv("1.csv", function(data) { data1 = data; init(); }); // ふたつ目 d3.csv("2.csv", function(data) { data2 = data; init(); }); // みっつ目 d3.csv("3.csv", function(data) { data3 = data; init(); }); // 初期化処理 function init() { // みっつの内、どれかが設定されてなかったら抜ける if (!data1 || !data2 || !data3) { return; } // みっつ全部そろったら処理を行う ... } ||<

javeloverさんのコメント
ありがとうございます。 1つ1つ、設定したほうがいいのですね なるほど、全部のデータがゲットできたら次に進むやり方ですね 試してみます。

javeloverさんのコメント
a-kuma3 さん
関連質問

●質問をもっと探す●



0.人力検索はてなトップ
8.このページを友達に紹介
9.このページの先頭へ
対応機種一覧
お問い合わせ
ヘルプ/お知らせ
ログイン
無料ユーザー登録
はてなトップ