質問: ### 前提・実現したいこと node.jsを利用し、複数のチームのSlackからチャンネル情報を取得して、 全チャンネル(絞り込みは追って対応するとして)の最終投稿を取得したいと考えています。 それらを選別・整形し、最終的にはHTMLで出力することを想定しています。 ### 試したこと asyncを利用して書いたりした物の、どうしても`for`や`forEach`が発生し、 同期処理と非同期処理(で良いもの)が入り乱れ、 何をどうしたら正しく処理が進むのか解らなくなってきてしまっています。 APIの取得は完了を待たねばならず、その辺りの解消法が一番重要だと思っています。 個別に質問をして解消を...と思った物の、 複数のAPI呼び出しが1つのネックになっている事も踏まえ、まとめた形で改めて質問を することにしました。 理想の流れとしては、 ``` // ここからtokenの数だけ繰り返し [API] 該当チームの情報取得 [API] 該当チームの全てのチャンネル情報を取得(プライベートチャンネルのみで良いのでgroupsが対象) // ここからチャンネルの数だけ繰り返し [API] 各チャンネルの最終投稿を取得 [API] 最終投稿のユーザ名などを取得 受け取ったユーザ名と投稿内容の結合 チャンネル情報と投稿の情報の結合 // ここまで // ここまで 全てのデータの結合(やもすれば、繰り返しの中でpushする?) 該当のデータ絞り込み作業 HTMLに整形 ``` だと思っています。 外部APIを利用するため、酷く躓いている気がするのですが、教えていただけたら幸いです。 --- #### 2017/02/20追記 どこまでやってみたかという部分なのですが、 結局の所、繰り返した実行結果をループの外側に持ち出すことで問題が起きている印象です。 若干混乱しており、何が何だか...という状態になっていることも否めないのですが、 一番外側の繰り返しについては、取得結果を繰り返し利用することも無く、無事に取得して、次の処理に渡す事はできています。 問題は、その取得したグループ情報を1つずつやりくりし、全ての結果を拾って1つの配列にまとめるという部分かと思っています。 うまくお伝えできているかわからず恐縮ですが、コレで、少し伝わったでしょうか...。
回答: 追記ありがとうございます。 頂いた内容を元に、やりたいと考えられているであろう内容を回答いたします。 ### 配列データに対して非同期処理を回し、結果を配列にして返す それぞれのグループ情報に対して非同期処理(今回はAPIを叩いてデータを取得する)を行い、 全ての結果を配列として格納するということなので、 ``` var async = require("async"); var https = require("https"); var group = [ {"id":"hoge"}, {"id":"fuga"}, {"id":"piyo"} ]; // 配列のそれぞれのデータが data に格納される async.map(group, (data, next) => { // API を叩く https.get("https://example.com/", (res) => { var body = ""; res.on("data", (chunk) => { body += chunk; }); res.on("end", () => { var json = JSON.parse(body); // 各データについて、結果を格納 next(null, body.data); }); }); }, (err, result) => { // next の第二引数で渡した値が、配列になって result に入ってくる console.log(result); }); ``` こんな感じになるのではと思います。 `async` を使用されているということですので、 `async.map` を使用しました。 これは、第一引数の配列データをそれぞれ第二引数の関数に渡し、 全てのデータに対して `next` が実行された時点で 第三引数の `result` にデータを配列にして入れてくれる、というものです。 今やりたいと思われていることと一致するのではないでしょうか。 ### 非同期処理を直列に配置する 非同期処理と同期処理が混ざって混乱されている部分もあるようですので、 その部分についても私なりの解決法を提案いたします。 こちらも `async` を利用されていますので、 `async.waterfall` を使用されるのが良いかと思います。 ``` var async = require("async"); var group = [ {"id":"hoge"}, {"id":"fuga"}, {"id":"piyo"} ]; async.waterfall([ (cb) => { // 非同期処理 async.map(group, (data, next) => { next(null, data.id); }, (err, result) => { cb(null, result) }); }, (arg, cb) => { // 同期処理 var sync = arg.map((data) => { return data + "!!!"; }); cb(null, sync); } ], (err, arg) => { console.log(arg); // [ 'hoge!!!', 'fuga!!!', 'piyo!!!' ] }); ``` `async.waterfall` は直列につなげた関数のコールバックに渡されたデータを、 次の関数で使用できる特徴があります。 ここで注意したいのは、(当然のことですが) `async.waterfall` 内での処理は 非同期処理を行う関数でなくとも良い点です。 それぞれの処理を(非同期・同期処理関係なく)関数にまとめて、 `return` の代わりにコールバックを呼び出す形にすることで 簡単に直列に繋ぐことができ、見通しもよくなるのではと思います。 すでに流れとして、動作を部品ごとに分ける作業はされているようですので、 あとはそれぞれを `async.waterfall` で繋ぐことで 期待する処理がかけるのではないでしょうか。 この回答で、お役に立てましたでしょうか。 もし、イメージされていることと異なっている場合は改めて追記いただくか、 新しく記事を立てていただけるとありがたいです。