SSブログ

複数の外部apiを叩く順序を制御する方法 [プログラミング]

JavaScriptやjQueryで外部apiを叩くコードを書いて遊んでいますが,複数のapiを叩いた場合に順番が期待どおりにならなかったので,いろいろ試してみました。

参考サイト1: http://kinopyo.com/ja/blog/jquery-ajax-get-order-guranteed-async-transfer
参考サイト2: http://qiita.com/jkr_2255/items/17693ab77beea71a871c唐突ですが,こんなコードを書いてみました。外部apiを7回叩いて為替レートをwebに表示する簡単なものですが。。。
<!DOCTYPE html>
<html lang="ja">

<head>
<meta charset="UTF-8"/>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
</head>

<body>
</body>

<script type="text/javascript">
jQuery(document).ready(function($){
  var ccy=["usdjpy", "eurjpy", "gbpjpy", "audjpy", "eurusd", "gbpusd", "audusd"];
  for(var i in ccy){
    $.getJSON("http://www.bloomberg.com/markets/api/quote-page/"+ccy[i]+":CUR?locale=en",function(json){
      document.write(ccy[i]+":"+json.basicQuote.price+"<br />");
    });
  }
});
</script>

</html>

これを走らせると,残念ながらこういう結果が返ってきました。為替レートは取得できているようですが,通貨表示がうまくいっていません。
undefined:161.435
undefined:110.31
undefined:122.61
undefined:1.4623
undefined:79.283
undefined:1.1115
undefined:0.7182

なぜ通貨表示がうまくいかないのかというと,$.getJSONによって外部apiを取得している間にforループが回り切ってi=7になってしまい,ccy[7]=undefinedとなったためです。つまり,jQueryのajax系関数は外部apiの戻りを待たずにどんどん先に進んでしまうというわけです。さて,どうしたもんでしょうか。

[1] jQueryの$.eachを使う

forループのインデックスが$.getJSONに紐付いていないことが一因なので,forループの代わりにjQueryの$.eachを使うという手があります。$.eachの中で定義した関数にはインデックスが引数として与えられるので,インデックスが勝手に進んでいかないというわけです。具体的には,
  for(var i=0; i<ccy.length; i++){


  $.each(ccy,function(i){

とすれば,結果はこうなります。とりあえずイメージどおりですが,通貨の順番が毎回安定しないのが不満です。
gbpjpy:161.435
usdjpy:110.31
eurjpy:122.61
gbpusd:1.4623
audjpy:79.283
audusd:0.7182
eurusd:1.1115


[2] 結果を貯めておいて一気に吐き出す

複数の外部apiを叩いた後にどの順番で戻ってくるかは,相手のある話なのでどうしようもありません。そこで,結果を貯めておいて揃ったら一気に吐き出すことにしてはどうでしょうか。具体的には,
<script type="text/javascript">
jQuery(document).ready(function($){
  var ccy=["usdjpy", "eurjpy", "gbpjpy", "audjpy", "eurusd", "gbpusd", "audusd"];
  var price=[];
  $.each(ccy,function(i){
    $.getJSON("http://www.bloomberg.com/markets/api/quote-page/"+ccy[i]+":CUR?locale=en",function(json){
      price[i]=json.basicQuote.price;
      if(price.length==ccy.length){
        for(j in ccy){
          document.write(ccy[j]+":"+price[j]+"<br />");
        }
      }
    });
  });
});
</script>

とすれば,結果はうまくいきました。
usdjpy:110.31
eurjpy:122.61
gbpjpy:161.435
audjpy:79.283
eurusd:1.1115
gbpusd:1.4623
audusd:0.7182


[3] 外部apiを叩く順序を制御する

一応満足のいく結果が得られましたが,全部揃うのを待つのでなく,そもそも外部apiを叩く順序を制御することはできないでしょうか。こんな感じで。
<script type="text/javascript">
jQuery(document).ready(function($){
  var ccy=["usdjpy", "eurjpy", "gbpjpy", "audjpy", "eurusd", "gbpusd", "audusd"];
  (function func(i){
    $.getJSON("http://www.bloomberg.com/markets/api/quote-page/"+ccy[i]+":CUR?locale=en",function(json){
      document.write(ccy[i]+":"+json.basicQuote.price+"<br />");
      if(i+1<ccy.length) func(i+1);
    });
  })(0);
});
</script>

こうすれば,前のajax呼び出しが終わるまでに次の呼び出しは発生しません。しかしかなり遅くなります^^; 実際にはそこまで厳密な順番を要求されないケースも多いでしょうから,二番目の方法とハイブリッドで行うのがよさそうですね。

[4] タイマーでずらしながら呼び出す

やや卑怯な方法ですが,setTimeout関数を使って時間差で呼び出すという手もあります。apiのおおよその戻り時間がわかっている場合に有効かも知れません。
<script type="text/javascript">
  var ccy=["usdjpy", "eurjpy", "gbpjpy", "audjpy", "eurusd", "gbpusd", "audusd"];
  $.each(ccy,function(i){
    setTimeout(function(){
      $.getJSON("http://www.bloomberg.com/markets/api/quote-page/"+ccy[i]+":CUR?locale=en",function(json){
        document.write(ccy[i]+":"+json.basicQuote.price+"<br />");
      });
    },i*10);
  });
});
</script>

最後の時刻を少しずつずらすのがポイントです。。。と言いたかったのですが,実際には終わりから4行目をi*0にしてもなぜかうまくいきます。なぜ?

[5] setTimeout関数で終了監視

同じsetTimeout関数を使うならば,処理がフリーズしないよう終了監視する手もあります。二番目の方法と精神的には同じですが,こちらの方が応用が利きやすそうです。
<script type="text/javascript">
jQuery(document).ready(function($){
  var ccy=["usdjpy", "eurjpy", "gbpjpy", "audjpy", "eurusd", "gbpusd", "audusd"];
  var price=[];
  $.each(ccy,function(i){
    $.getJSON("http://www.bloomberg.com/markets/api/quote-page/"+ccy[i]+":CUR?locale=en",function(json){
      price[i]=json.basicQuote.price;
    });
  });
  //ここから終了監視
  (function complete(){
    if(price.length!=ccy.length){
      setTimeout(complete,0);
      return;
    }
    for(j in ccy){
      document.write(ccy[j]+":"+price[j]+"<br />");
    }
  })();
});
</script>


おしまい。
nice!(0)  コメント(0)  トラックバック(0) 

nice! 0

コメント 0

コメントを書く

お名前:
URL:
コメント:
画像認証:
下の画像に表示されている文字を入力してください。

※ブログオーナーが承認したコメントのみ表示されます。

トラックバック 0

この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。