115
Help us understand the problem. What are the problem?

More than 1 year has passed since last update.

posted at

updated at

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

はじめに

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

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

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

Promiseとは

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

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

Promiseの基本

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

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()の場合の処理を受ける。

複数の値を受け渡す

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
  });

実行パターン

順番に実行 - その1 (直列)

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

// 上から順番に処理を実行する
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 完了!

順番に実行 - その2 (直列)

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

// 上から順番に処理を実行する
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()で実行する関数に引数を渡せます。

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);
  });
}

同時に実行 - その1 (並列)

並列処理は、Promise.all()を使います。

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 (並列)

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

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などのpolyfillを使います。

おわりに

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

なお、iOS9はES6に対応していないので、ネイティブのPromise実装はできません。
各ブラウザの対応状況は、こちらで確認できます。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
115
Help us understand the problem. What are the problem?