LoginSignup
29
29

More than 5 years have passed since last update.

Array.reduce を使って配列に対する同期処理をよりエレガントにする

Last updated at Posted at 2016-02-08
  • 大量のデータを順番にサーバーに送りたい。
  • 大量のデータをブラウザのデータベースに順番に保存したい。

等、配列に収められた一連のデータを、順番に (同期的に) 処理したいけど、非同期処理が含まれてしまうため、タイミング調整するのが難しい、というケースが有ります。

解決法のひとつとして、Promise を使った同期処理 という記事を以前書いたのですが、Array.reduce を使った例が非常にエレガントだったので、分かりやすく?紹介します。

参考資料:Promise waterfall

Array.reduce() は配列の中身を順番に関数に投げ、結果を累積的に処理していくためのものです。例えば

var result = [1, 2, 3, 4].reduce(function(prevResult, currValue, index, array) {
  return prevResult + currValue;
}, 0);
console.log(result);

.reduce の第一引数となる関数は 4 つの引数を取ります。

  • 前回の結果
  • 今回検証する配列の値
  • 今回検証する配列の番号
  • 配列そのもの

.reduce の第二引数となる値は、最初の検証時の 前回の結果 を指定することができます。省略すると、インデックス 0 がこれに割り当てられ、実際の検証はインデックス 1 からスタートすることになります。上記の例では

順番 前回の結果 今回検証する配列の値 今回検証する配列の番号 配列そのもの
1回目の検証 0 1 0 [1,2,3,4]
2回目の検証 1 2 1 [1,2,3,4]
3回目の検証 3 3 2 [1,2,3,4]
4回目の検証 6 4 3 [1,2,3,4]

というわけで、結果は 10 ということになります。

これを応用して毎回 Promise を返すことでチェーンさせる例が下記になります。

var wait = function(val) {
  return new Promise(resolve => {
    // 1秒待ちを入れてコンソールに数字を表示する
    // XHR 等の非同期処理でも良い
    setTimeout(() => {
      console.log(val);
      resolve();
    }, 1000);
  });
};

// 配列を順番に処理する
var sequential = function(array) {
  // .reduce で順番に処理。初期値は第二引数の Promise.resolve()
  return array.reduce((promise, val) => {
    // promise が常に前回の戻り値であることを利用して
    // Promise を連鎖させるのがポイント
    return promise.then(res => wait(val));
  }, Promise.resolve());
}

// 実行すると Console に 10 からのカウントダウンが 1 秒置きで表示される
sequential([10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]);

動くサンプル

29
29
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
29
29