[lodash] [async.js] jsのいろいろなforEachを試してみた

## はじめに
javascriptでforEachを書くパターンは色々あります
使用していて「このループを書くならこっちのほうがいいな」「このforEachって非同期処理のときどうなるんだっけ」と思い返すことが多々あったので記事にまとめておこうと思いました。
今回取り上げるのは以下です
・ Array.prototype.forEach
・ lodash forEach
・ async.js each
・ async.js eachSeries

サンプルコード内で非同期関数 `asyncFunc`, `arr` を呼び出していますが、これはそれぞれ

``` javascript
const arr = [1, 2, 3];

// 一秒後に引数の関数を実行する関数
const asyncFunc = i => {
  setTimeout(() => {
    console.log(`output: ${i}`);
  }, 1000);
}
```
このように定義しています。

## Array.prototype.forEach
[Array.prototype.forEach documentation](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach)

``` javascript
arr.forEach(element => {
  asyncFunc(element)
});

/**
 * 一秒後にまとめて結果が返ってくる
output: 1
output: 2
output: 3
 */
```

`Array.prototype.forEach` はまさにノンブロッキングの形になっていて、forEach内の要素が順にイベントキューに積まれて行くといったイメージでしょうか
ちなみに、 `Array.prototpye.forEach` はcontinueは可能(returnするだけ)ですが、**breakは不可能**です
また、要素を同期的に実装するのも厳しいです。(自分の知る限りでは)

## lodash forEach
[lodash documentation](https://lodash.com/docs/4.17.15#forEach)

``` javascript
const _ = require('lodash');
_.forEach(arr, element => {
  asyncFunc(element)
});

/**
 * 一秒後にまとめて結果が返ってくる
output: 1
output: 2
output: 3
 */
```
こちらも `Array.prototype.forEach` と同様に要素がすべてイベントキューに積まれていく形です。
ただちょっと違うのは、 **breakができる** という点です

``` javascript
const _ = require('lodash');
_.forEach(arr, element => {
  console.log(`start: ${element}`)
  asyncFunc(element)
  if (element === 1) return false;
});

/**
 * 1だけ出力してbreakされている
start: 1
output: 1
 */
```

このように、イベントキューに1,2,3それぞれの非同期処理が積まれていても、1の処理でbreakされれば後続の処理はされずに終了します。
また、 `Array.prototype.forEach` と同様に同期的に実行するのも厳しいです
小ネタとしてはdocumentにも書かれていますが**Objectに対しても実行可能**です。

```javascript
_.forEach({ 'a': 1, 'b': 2 }, function(value, key) {
  console.log(`${key}:${value}`);
});
/**
 * objectに対しても利用可能
a:1
b:2
 */
```

## async.js each
[async documentation](https://caolan.github.io/async/v3/docs.html#each)

``` javascript
async.each(arr, (elem, next) => {
  asyncFunc(elem)
  next();
}, (err) => {
  console.log(err);
});

/**
 * 一秒後にまとめて結果が返ってくる
output: 1
output: 2
output: 3
 */
```
こちらも同様に要素がすべてイベントキューに積まれていく形です。
breakの機能はありませんが、callbackにエラーを投げることが出来ます。

```javascript
// 実行要素の途中でエラーが発生した場合
async.each(arr, (elem, next) => {
  console.log(`start: ${elem}`)
  asyncFunc(elem)
  if (elem >= 2) return next('err occured');
  else return next();
}, (err) => {
  if (err) console.log(err)
});

/**
 * エラーが発生したタイミングでcallbackが呼び出されるが、asyncFuncは全て実行される
start: 1
start: 2
err occured
start: 3
1
2
3
 */

// 実行要素がすべてエラーを返さなかった場合
async.each(arr, (elem, next) => {
  console.log(`start: ${elem}`)
  asyncFunc(elem)
  return next();
}, (err) => {
  if (err) console.log(err)
  else console.log('error not occured')
});

/**
 * すべての処理をキューに詰め込んだあとでcallbackが呼び出される
start: 1
start: 2
start: 3
error not occured
1
2
3
 */
```
↑のように、イベントキューに積まれている非同期処理はその途中でエラーが発生(nextの第一引数にエラー内容を指定)しても関係なくすべて実行されます。また、エラーが発生した際はその直後にcallback関数(第三引数)が呼び出されます。
一度もエラーが発生しなかった際は、すべての処理をイベントキューに詰め込んだあとでcallbackが呼び出されます。
ちょっと特殊っぽい。

## async.js eachSeries

``` javascript
async.eachSeries(arr, (elem, next) => {
  asyncFunc(elem)
  return next();
}, (err) => {
  if (err) console.log(err)
});

/**
 * 一秒後にまとめて結果が返ってくる
output: 1
output: 2
output: 3
 */
```

先程の `async.js each` の同期的バージョンです。  `each` では処理の順番は保証されていませんでしたが、 `eachSeries` では実行順が保証されています。


またそのような性質から このように `next` を活用することで同期的にすることも可能です。

``` javascript
const _asyncFunc = (i, cb) => {
  setTimeout(() => {
    console.log(i);
    return cb();
  }, 1000);
}

async.eachSeries(arr, (elem, next) => {
  _asyncFunc(elem, next)
}, (err) => {
  if (err) console.log(err)
});

/**
 * 一秒ごとに出力される
output: 1
output: 2
output: 3
 */
```


## まとめ
まとめてみると、意外とそれぞれで違いがあることがわかりました。
それでは