Promiseとthenのメソッドチェーン（直列・並列・値の受け取り・引数）

# はじめに
Promiseの基本とthen()を使ったメソッドチェーンの使い方で、以下を中心にまとめています。

- 複数の非同期処理を**順番に実行したい**（**直列**）、同時に実行したい（**並列**） 
- Promiseの直列処理で、**複数の値を受け取りたい**
- then()を使ったメソッドチェーンで、関数の呼び出しに**引数を渡したい**

これからPromiseを使いたい人の参考になれば幸いです。


# Promiseとは
非同期処理の結果を、成功（**resolve**） または、失敗（**reject**）で返すオブジェクトです。
Promiseを使用すると、以下のような非同期処理を簡潔に書けます。

- 非同期処理の成功、失敗の処理を分岐する。
- **複数の非同期処理を順番に実行**したり、並行して実行する。（直列・並列）


# Promiseの基本
```new Promise```でインスタンスを生成して使用します。
Promiseの引数には関数を指定し、その関数内に実行したい非同期処理を記述します。

```javascript
var myPromise = new Promise(function (resolve, reject) {
  // 実行したい処理を記述
  setTimeout(function() {
    // 成功
    resolve('成功!'); // resolve(渡したい値)
  }, 3000);
});


myPromise
  .then(function(value) {
    // 非同期処理が成功した場合
    console.log('実行結果:' + value); // => 実行結果:成功!
  })
  .catch(function(value) {
    // 非同期処理が失敗した場合
    console.log('実行結果:' + value); // 呼ばれない
  });
```

- `function (resolve, reject) {...}`を実行した後に、`new Promise`を実行。
- `function (resolve, reject) {...}`の処理が成功の場合は`resolve()`を、失敗の場合は`reject()`を明示してインスタンスに渡す。
- `.then()`で`resolve()`の場合、`.catch()`で`reject()`の場合の処理を受ける。


## 複数の値を受け渡す
```javascript:resolve()で複数の値を渡す
// 複数の値を配列で渡す
var myPromise2 = new Promise(function (resolve, reject) {
  // 複数の値を渡す場合は、配列にまとめる
  resolve([123, 'abc']); // resolve([値1, 値2...])
});


myPromise2
  .then(function(value) {
    console.log(value[0]); // => 123
    console.log(value[1]); // => abc
  });
```

# 実行パターン
## 順番に実行 - その１ （直列）
Promiseは`then()`メソッドを使って、複数の処理をつなぎ、順番に実行することが出来ます。
以下はthen()で繋いだPromiseのメソッドチェーン。

```javascript
// 上から順番に処理を実行する
Promise.resolve()
.then(function(){
  return new Promise(function (resolve, reject) {
    setTimeout(function() {
      console.log('task1 処理中...');
      resolve('task1 完了!');
    }, 1000);
  });
})
.then(function(value){
  return new Promise(function (resolve, reject) {
    setTimeout(function() {
      console.log(value); // => task1 完了!
      console.log('task2 処理中...');
      resolve(['task2 完了!', 123]); // 複数の値を渡す時は、配列にまとめる
    }, 3000);
  });
})
.then(function(value){ // resolveで渡された値の受け取り  => ['task2 完了!', 123]
  return new Promise(function (resolve, reject) {
    console.log(value[0]); // => task2 完了!
    console.log(value[1]); // => 123
    console.log('task3 完了!');
    resolve();
  });
});
```
- `resolve()`で渡した値は、次の`.then(function(value){...})`内のvalueで受け取ります。

setTimeoutで時間差をつけても、以下のように順番に実行されます。

```:実行結果
task1 処理中...
task1 完了!
task2 処理中...
task2 完了!
123
task3 完了!
```


## 順番に実行 - その２ （直列）
直列処理は以下のように、then()と関数を分けて書くこともできます。
（実行結果は「複数の非同期処理を順番に実行 - その１ （直列）」と同じ）
引数を指定しない場合`then(実行したい関数名)`で()は不要です。

```javascript
// 上から順番に処理を実行する
Promise.resolve()
  .then(task1) // .then(実行したい関数名)
  .then(task2)
  .then(function(value){ // 直に処理を直接書いてもOK
    return new Promise(function (resolve, reject) {
      console.log(value[0]);
      console.log(value[1]);
      console.log('task3 完了!');
      resolve();
    });
  });


function task1(){
  return new Promise(function (resolve, reject) {
    setTimeout(function() {
      console.log('task1 処理中...');
      resolve('task1 完了!');
    }, 1000);
  });
}

function task2(value){
  return new Promise(function (resolve, reject) {
    setTimeout(function() {
      console.log(value);
      console.log('task2 処理中...');
      resolve(['task2 完了!', 123]); // 複数の値を渡す時は、配列にまとめる
    }, 3000);
  });
}
```

### then()で呼び出す関数に引数を渡す
`bind()`を使えば、`then()`で実行する関数に引数を渡せます。

```javascript
Promise.resolve()
  .then(task1.bind(null, '123'))
  .then(task2.bind(null, ['aaa', 'bbb'])) // bindでtask2の第1引数を固定
  .then(function(value){
    return new Promise(function (resolve, reject) {
      console.log(value);
      console.log('task3 完了!');
      resolve();
    });
  })


function task1(msg){
  return new Promise(function (resolve, reject) {
    setTimeout(function() {
      console.log('task1 処理中...');
      console.log(msg); // => 123
      resolve(['task1 完了!', msg]);
    }, 1000);
  });
}

// thenでtask2を呼び出す時に、第一引数をbindで固定しているため、値の受け取りは以下になる
// 第1引数 args  => ['aaa','bbb']
// 第2引数 value => ['task1 完了!', '123']
function task2(args, value){
  return new Promise(function (resolve, reject) {
    setTimeout(function() {
      console.log(value[0]);  // => task1 完了!
      console.log(value[1]);  // => 123
      console.log(args[0]);   // => aaa
      console.log(args[1]);   // => bbb
      console.log('task2 処理中...');
      resolve('task2 完了!');
    }, 3000);
  });
}
```

## 同時に実行 - その１ （並列）
並列処理は、`Promise.all()`を使います。

```javascript
Promise.all([
  taskA(),  //同時に実行したい関数名
  taskB(),
  taskC()
]);

function taskA(){
  return new Promise(function (resolve, reject) {
    setTimeout(function() {
      console.log('taskA 完了!');
      resolve();
    }, 1000);
  });
}

function taskB(){
  return new Promise(function (resolve, reject) {
    setTimeout(function() {
      console.log('taskB 完了!');
      resolve();
    }, 3000);
  });
}

function taskC(){
  return new Promise(function (resolve, reject) {
    console.log('taskC 完了!');
    resolve();
  });
}
```

```:実行結果
taskC 完了!
taskB 完了!
taskA 完了!
```

## 同時に実行 - その２ （並列）
並列処理は以下のように、関数にまとめることもできます。
（実行結果は「同時に実行 その2 （並列）」と同じ）

```javascript
Promise.resolve()
  .then(parallel) // 関数にまとめた並列処理を実行

function parallel() {
  return Promise.all([
    taskA(),  //同時に実行したい関数名
    taskB(),
    taskC()
  ]);
}

function taskA(){
  return new Promise(function (resolve, reject) {
    setTimeout(function() {
      console.log('taskA 完了!');
      resolve();
    }, 1000);
  });
}

function taskB(){
  return new Promise(function (resolve, reject) {
    setTimeout(function() {
      console.log('taskB 完了!');
      resolve();
    }, 3000);
  });
}

function taskC(){
  return new Promise(function (resolve, reject) {
    console.log('taskC 完了!');
    resolve();
  });
}
```

# IE11でPromiseを使う
IE11はPromiseに対応していません。
Promiseを使いたい場合は、[es6-promise](https://www.npmjs.com/package/es6-promise)などのpolyfillを使います。


# おわりに
説明を省略している部分もありますが、実際にPromiseを使う上で必要になりそうなことは、まとめることが出来たと思います。

なお、iOS9はES6に対応していないので、ネイティブのPromise実装はできません。
各ブラウザの対応状況は、[こちら](https://caniuse.com/#feat=promises)で確認できます。