コールバック関数を使った非同期処理は、コールバック地獄の懸念があることを以前当ブログでも取り上げました。
しかし、ES6からはPromiseという新しい非同期処理の仕組みが導入され、コールバック地獄の問題が解消されました。

Promiseは、コールバック関数に比べて、処理のフローを細かく制御することができ、非同期処理には幅広く使われています。そこで今回は、Promiseを使った非同期処理の方法を解説していきます。

Promiseとは

「Promise」とは、非同期処理の結果を保持するオブジェクトのことです。
また、そのPromiseオブジェクトを利用した処理の仕組みのことを指します。

Promiseは、以下の3つのステータスを持っています。
・「Pending」:初期の状態、または処理待ちの状態
・「Fullfilled」:処理が成功して完了した状態
・「Rejected」:処理が失敗した状態

これらのPromiseオブジェクトのステータスによって、処理を分岐していくことがPromiseの特徴です。

Promiseオブジェクトの生成

以下はPromiseオブジェクトを生成するための構文です。
Promiseの引数には2つの関数を渡し、第1引数には「resolve」、第2引数には「reject」を渡します。

const promise = new Promise((resolve, reject) => {});

処理が成功ならresolve、失敗ならrejectのPromiseオブジェクトを返します。

const promise = new Promise((resolve, reject) => {
  //何かしらの処理
  const result = something();
  //処理に成功ならresolve、失敗ならreject
  if (result === 'success') {
    resolve(result);
  } else {
    reject('fail')
  }
});

thenメソッド

「thenメソッド」は、fullfilledステータス、またはrejectedステータスのPromiseオブジェクトを受け取ることができます。

resolveされた時にはその成功した結果を受け取り、rejectされた時にはそのエラーを受け取ります。

resolveされた時

const promise = new Promise((resolve) => {
  resolve('成功しました');
}).then(result => console.log(result));
//成功しました

resolveの場合は、resolveの引数がthenの第1引数に渡ります。
つまり、resolveの引数「’成功しました’」と言う文字列が、thenの引数「result」に引き継がれることになります。

rejectされた時

const promise = new Promise((resolve, reject) => {
  reject('失敗しました');
}).then(
  result => console.log(result), //実行されない
  error => console.log(error) //rejectの引数が渡される
);
//失敗しました

一方、rejectの場合は、thenの引数に2つの関数を用意することが必要です。
しかし、実際に実行されるのは2番目の関数です。
rejectの引数「’失敗しました’」と言う文字列が、thenの2番目の引数「error」に引き継がれることになります。

実際のプログラムでは、resolveされた時とrejectされた時の処理をまとめて書くこともあります。
これらを合わせたthenの使い方も見てみましょう。

サンプル

const validation = (password) => {
  return new Promise((resolve, reject) => {
    if (password.length >= 10) {
      resolve({
        password: password,
      });
    } else {
    reject('パスワードは10文字以上で入力してください')
    }
  });
};

let password = '123456789';

validation(password).then(
  result => console.log(result), //バリデーションが成功した時の処理(resolve)
  error => console.log(error) //バリデーションが失敗した時の処理(reject)
);

実行結果

//'パスワードは10文字以上で入力してください'

 

reject

ここでは、「let password = ‘123456789’」とあえて9文字のパスワードで変数宣言しています。
そのため、if文でパスワードの文字数が10文字以上でない場合のrejectに回り、thenのerrorが返されたということになります。

試しに、パスワードを「let password = 1234567890」と10文字に設定し直してみるとどうでしょう。

実行結果

//{password: "1234567890"}

resolve

パスワードが10文字以上のため、バリデーションが成功した場合の処理を実行することが分かります。

catchメソッド

「catchメソッド」は、rejectedステータスのPromiseオブジェクトを受け取ります。
かんたんに言うと、エラー処理専用のメソッドです。

実質的には、thenメソッドでrejectedステータスを扱う場合と同じですが、コードが簡潔化されます。

const promise = new Promise((resolve, reject) => {
  reject('失敗しました');
}).catch(error => console.log(error));
//失敗しました

Promiseチェーン

処理に失敗した時は、catchに書かれた処理を実行することができますが、例えば、その処理の後にまた別の新しい処理を実行したい場合もあるでしょう。

catchメソッドは、thenメソッドと組み合わせて使用することもでき、以下のように制御することができます。

const promise = new Promise((resolve, reject) => {
  reject();
})
  .then(() => {
    console.log('resolve');
  })
  .catch(() => {
    console.log('error');
  })
  .then(() => {
    console.log('resolve again');
  });
//error
//resolve again

rejectされたため、catchに書かれた処理が実行され、その後に2つ目のthenの処理が実行されました。

このようにcatch、then、catchとメソッドをつなぎ合わせていくことを、「Promiseチェーン」と呼びます。

Promiseチェーンをうまく活用することで、コールバック関数のようにネストが深くならず、複数の非同期処理を制御することができます。
また、どのような処理を行っているのか分かりやすく、可読性も上がります。

まとめ

今回はPromiseを使った非同期処理の方法を解説しました。

・Promiseはステータスによって処理を制御する
・fullfilledとrejectedステータスのPromiseオブジェクトを受け取るthenメソッド
・rejectedステータスのPromiseオブジェクトを受け取るcatchメソッド

Promiseでできることは多岐に渡り、習得するのは容易ではありません。
しかし、より良いコードフローを実現することができるため、少しずつ理解に繋げていきましょう。

JavaScript関連記事