Promiseの直列処理をループする（完了後に実行したい処理もある）

# はじめに

JavaScriptでPromiseを使い始めたとき、出来ると便利な以下の場合について。

- Promiseで順番に実行する**直列処理を、ループで繰り返す**。
- **ループ完了後に実行したい処理がある。**


# Promiseをループで繰り返す
まず最初に、**Promiseの処理をforループで繰り返す**基本パターン。

```javascript
// Promiseの処理をループで繰り返す
var myPromise = Promise.resolve();
for(var i = 0; i < 3; i++) {
  myPromise = myPromise
  .then(task1.bind(this, i)) // bindでtask1に引数iを渡す
}


// ループで実行する処理
function task1(val){ // 引数iを受け取る
  return new Promise(function(resolve, reject) {
    setTimeout(function(){
        console.log('task1 : ' + val);
        resolve();
     }, 1000);
  });
}
```

```:実行結果
task1 : 0
task1 : 1
task1 : 2
```


# 実行パターン

## 直列処理を繰り返す
メソッドチェーンでつないだ、Promiseの直列処理をループで繰り返すパターン。

```javascript
// Promiseの直列処理をループで繰り返す
var myPromise = Promise.resolve();
for(var i = 0; i < 3; i++) {
  myPromise = myPromise
  .then(task1.bind(this, i)) // bindでtask1に引数iを渡す
  .then(task2)
}


// ループで実行する処理
function task1(val){ // 引数iを受け取る
  return new Promise(function(resolve, reject) {
    setTimeout(function(){
        console.log('task1 : ' + val);
        resolve(val); // task2へ値を渡す
     }, 1000);
  });
}

function task2(res){ // task1から値を受け取る
  return new Promise(function(resolve, reject) {
    setTimeout(function(){
        console.log('task2 : ' + res);
        resolve();
     }, 1000);
  });
}
```

```:実行結果
task1 : 0
task2 : 0
task1 : 1
task2 : 1
task1 : 2
task2 : 2
```


## ループ完了後に実行
メソッドチェーンでつないだ、Promiseの直列処理をループで繰り返し、**ループ完了後に処理を実行する。**

```javascript
// Promiseの直列処理をループで繰り返す
var myPromise = Promise.resolve();
for(var i = 0; i < 3; i++) {
  myPromise = myPromise
  .then(task1.bind(this, i)) // bindでtask1に引数iを渡す
  .then(task2)
}
myPromise
.then(function(){
  return new Promise(function (resolve, reject) {
    // ループ完了後に実行したい処理
    console.log('ループ完了後に実行');
    resolve();
  });
})


// ループで実行する処理
function task1(val){ // 引数iを受け取る
  return new Promise(function(resolve, reject) {
    setTimeout(function(){
        console.log('task1 : ' + val);
        resolve(val); // task2へ値を渡す
     }, 1000);
  });
}

function task2(res){ // task1から値を受け取る
  return new Promise(function(resolve, reject) {
    setTimeout(function(){
        console.log('task2 : ' + res);
        resolve();
     }, 1000);
  });
}
```

```:実行結果
task1 : 0
task2 : 0
task1 : 1
task2 : 1
task1 : 2
task2 : 2
ループ完了後に実行
```


## ループで繰り返す処理をまとめる
ループで繰り返す直列処理`task1()`と`task2()`を`taskAll()`にまとめたパターン。
実行結果は同じです。

```javascript
// Promiseの直列処理をループで繰り返す
var myPromise = Promise.resolve();
for(var i = 0; i < 3; i++) {
  taskAll(i);
}
myPromise
.then(function(){
  return new Promise(function (resolve, reject) {
    // ループ完了後に実行したい処理
    console.log('ループ完了後に実行');
    resolve();
  });
})


// ループで実行する処理
function taskAll(i){
  myPromise = myPromise
  .then(task1.bind(this, i)) // bindでtask1に引数iを渡す
  .then(task2)
}

function task1(val){ // 引数iを受け取る
  return new Promise(function(resolve, reject) {
    setTimeout(function(){
        console.log('task1 : ' + val);
        resolve(val); // task2へ値を渡す
     }, 1000);
  });
}

function task2(res){ // task1から値を受け取る
  return new Promise(function(resolve, reject) {
    setTimeout(function(){
        console.log('task2 : ' + res);
        resolve();
     }, 1000);
  });
}
```

```:実行結果
task1 : 0
task2 : 0
task1 : 1
task2 : 1
task1 : 2
task2 : 2
ループ完了後に実行
```

`taskAll()`に直列処理だけではなく、`Promise.all()`で並列処理を組み合わせることも出来ます。

# おわりに
他にも使いそうな便利なパターンがあれば追記します。

※ 直列処理や引数の扱いについては、[Promiseとthenのメソッドチェーン（直列・並列・値の受け取り・引数）](https://qiita.com/saka212/items/9b6cfe06b464580c2ee6)を参照。