(追記)何故駄目なのかを追記しました。
forEachの中では問題なくconsole.logで出力出来ているのですが、forEachを出てしまうと、下記コード内jsonTeamが出力出来なくなってしまっています。
forEach
では解決出来ません。
Node.jsのコールバック周りの仕様を把握しましょう。
ざっくり解説すると、Node.jsやJavaScriptは元々シングルスレッドなのでHTTP通信等の他にボトルネックのある作業中はリソースの無駄ですよね。
それは嫌なので、通信系のメソッドにはコールバック関数を設定して、
「通信終わったら、引数として渡しておいたこの関数実行しといてね。それじゃ俺は下の処理を実行するから〜」
…という命令を出した後、通信の結果を待たずに下の行の処理を実行し始めます。
この仕様により、例えば5箇所のAPIにリクエストを投げて、
その結果を配列で受け取るというのは普通のfor文では不可能です。
5個のリクエストを同時に発射してそのまま下の行を実行するので、下の方でconsole.log(jsonTeam);
とすると空の配列になってしまうのです。
なので、普通にやった場合、
1回目のリクエストのコールバックに2回目のリクエストを発射するという風に、
関数を5重にネストして、その後のコールバック関数内でconsole.log(jsonTeam);
とすれば要素がちゃんと5つ入った配列を取得出来るのですが、
そんなことしたくないですよね…?
5リクエストが固定ならまだしも、4つや6つに増えたり減ったりする場合また書き直しかよ…
というわけで、何かしらの特殊なラッパーが必要になります。
JavaScriptは如何にしてAsync/Awaitを獲得したのか Qiita版
これは目を通してください。
頭から読めばしっくり来る答えにたどり着けると思います。
下記はasyncを利用した解決方法です。
思いつかなければこれでとりあえずいいんじゃないですかね?
JavaScript
1var http = require('http');
2var https = require('https');
3var Slack = require('slack-node');
4var async = require("async");
5
6var apiTokens = [], getTokens;
7// apiTokens.push(xxx);を繰り返す
8
9console.log('start');
10
11// 全部コールバックの関数にしてしまう
12getTokens = apiTokens.map(function(apiToken){
13 return function(next){
14 var slack = new Slack(apiToken);
15 slack.api("team.info", function(err, response) {
16 delete(response.team.email_domain);
17 var icon = response.team.icon.image_original;
18 delete(response.team.icon);
19 response['team']['icon'] = icon;
20 next err, response.team;
21 })
22 }
23})
24
25// 第二引数の関数の第二引数に、上のnextの第二引数の配列が入ってくる。
26async.series(getTokens, function(err, Teams){
27 console.log(JSON.stringify(Teams, null, 2));
28})
追記
Array.mapはMDNにあるリスト操作系のメソッドです。
配列の全ての要素に同じ処理を適用したものを返します。
forEachとは同じリスト操作系メソッドの兄弟みたいなものですが、forEachはサブルーチンなので処理した結果は捨てられます。
mapは処理した結果を戻り値として持ち帰って来ますので、特性からふさわしい方を使いわける事が一般的です。
JavaScript
1var add_1 = function(it){ return it + 1; };
2[1, 2, 3, 4, 5].map(add_1);
3// [2, 3, 4, 5, 6]
4
5var fns = [1, 2, 3, 4, 5].map(function(it){
6 return function(){
7 console.log(it);
8 };
9});
10// [fn, fn, fn, fn, fn]
11
12fns[0]();
13// 1
更に追記:mapとasyncを併用して可変の要素を順番に実行
async.seriesのページを見ると捗りそうですのでリンクを貼ります。
JavaScript
1var functions = [
2 function (callback) { callback null, 1; },
3 function (callback) { callback null, 2; }
4];
5async.series(functions, function(err, results){
6 console.log(results);
7 // [1, 2]
8});
これの仕組みを応用して作ったのが上記コードです(一部Typoがあったので修正済)
apiToken変数を束縛しながら、async.seriesが望むコールバック関数を持つ配列に加工しています。
JavaScript
1getTokens = apiTokens.map(function(apiToken){
2 return function (callback) {
3 // apiTokenを利用してSlackと通信
4 callback null, 「通信結果」;
5 }
6})
7
8console.log(getTokens);
9// [
10// function (callback) { callback null, 「通信結果1」; },
11// function (callback) { callback null, 「通信結果2」; },
12// function (callback) { callback null, 「通信結果3」; },
13// function (callback) { callback null, 「通信結果4」; },
14// function (callback) { callback null, 「通信結果5」; }
15// ]
16
17async.series(getTokens, function(err, results){
18 console.log(results);
19 // [「通信結果1」, 「通信結果2」, 「通信結果3」, 「通信結果4」, 「通信結果5」]
20});
※このやり方はあくまで発展途上のものの一つです。
なので動作確認後、必ず他の回答者さんの答えや
JavaScriptは如何にしてAsync/Awaitを獲得したのか Qiita版の記事を読んで上手いやり方を模索してみてください。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2017/02/13 07:58