複数の外部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に表示する簡単なものですが。。。
これを走らせると,残念ながらこういう結果が返ってきました。為替レートは取得できているようですが,通貨表示がうまくいっていません。
なぜ通貨表示がうまくいかないのかというと,$.getJSONによって外部apiを取得している間にforループが回り切ってi=7になってしまい,ccy[7]=undefinedとなったためです。つまり,jQueryのajax系関数は外部apiの戻りを待たずにどんどん先に進んでしまうというわけです。さて,どうしたもんでしょうか。
jQueryの$.eachを使う
forループのインデックスが$.getJSONに紐付いていないことが一因なので,forループの代わりにjQueryの$.eachを使うという手があります。$.eachの中で定義した関数にはインデックスが引数として与えられるので,インデックスが勝手に進んでいかないというわけです。具体的には,
を
とすれば,結果はこうなります。とりあえずイメージどおりですが,通貨の順番が毎回安定しないのが不満です。
結果を貯めておいて一気に吐き出す
複数の外部apiを叩いた後にどの順番で戻ってくるかは,相手のある話なのでどうしようもありません。そこで,結果を貯めておいて揃ったら一気に吐き出すことにしてはどうでしょうか。具体的には,
とすれば,結果はうまくいきました。
外部apiを叩く順序を制御する
一応満足のいく結果が得られましたが,全部揃うのを待つのでなく,そもそも外部apiを叩く順序を制御することはできないでしょうか。こんな感じで。
こうすれば,前のajax呼び出しが終わるまでに次の呼び出しは発生しません。しかしかなり遅くなります^^; 実際にはそこまで厳密な順番を要求されないケースも多いでしょうから,二番目の方法とハイブリッドで行うのがよさそうですね。
タイマーでずらしながら呼び出す
やや卑怯な方法ですが,setTimeout関数を使って時間差で呼び出すという手もあります。apiのおおよその戻り時間がわかっている場合に有効かも知れません。
最後の時刻を少しずつずらすのがポイントです。。。と言いたかったのですが,実際には終わりから4行目をi*0にしてもなぜかうまくいきます。なぜ?
setTimeout関数で終了監視
同じsetTimeout関数を使うならば,処理がフリーズしないよう終了監視する手もあります。二番目の方法と精神的には同じですが,こちらの方が応用が利きやすそうです。
おしまい。
参考サイト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の戻りを待たずにどんどん先に進んでしまうというわけです。さて,どうしたもんでしょうか。
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
結果を貯めておいて一気に吐き出す
複数の外部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
外部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呼び出しが終わるまでに次の呼び出しは発生しません。しかしかなり遅くなります^^; 実際にはそこまで厳密な順番を要求されないケースも多いでしょうから,二番目の方法とハイブリッドで行うのがよさそうですね。
タイマーでずらしながら呼び出す
やや卑怯な方法ですが,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にしてもなぜかうまくいきます。なぜ?
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>
おしまい。
2016-05-28 22:14
nice!(0)
コメント(0)
トラックバック(0)
コメント 0