JavaScript

Promise入門【JavaScript】

今日は、新米エンジニアにとっての大きな壁である、Promiseをとりあげます。エンジニアの採用面接でもよく聞かれるトピックです。JavaScriptを扱うならば、早めに苦手意識を克服しておきましょう。シンプルなコードで見れば、「Promiseという何か複雑な響き…」よりも、ずっとシンプルなので、ビビらずにいきましょう!

Contents

Promiseとは何か?

まずは、Promiseとは何か。言葉で説明します。

Promise とは、オブジェクトであり、非同期処理の結果を返します。非同期処理ですので、未来に帰ってくる結果です(値が返ってくるまでに時間がかかるイメージです。)。また、結果とは、処理が、成功したとか失敗したとか、はたまた、成功し返ってきた値などをさします。

Promiseは以下の3つの状態を持ちます。

  1. pending: 初期状態。まだ、処理が完了していない状態。
  2. fullfilled: 処理が成功して完了した。
  3. rejected: 処理が失敗した。 

言葉である程度説明できることは、面接などでは大切ですので、以下のコード例を理解した後に、もう一度読んでみてください。

簡単なPromiseを自分で作り、.then()でPromiseを処理するパターン

この例では、自分で簡単なPromiseを定義し、.then()メソッドを利用して、処理しています。コード内のコメントで説明します。

const customMadePromise = new Promise((resolve, reject) => {
 // aは常にtrueなので、このPromiseの処理は常に成功します。
 const a = true;
  
  if (a) {
   // 処理が成功したら、'Sucess'という文字列を返すと定義する。
   resolve('Sucess');
  } else {
   // 処理が失敗したら、'Failed'という文字列を返すと定義する。
   reject('Failed');
  }
}) 

// Promiseを処理。
customMadePromise.then(res => {
 // Promiseが成功した時に呼ばれる関数ブロック
 console.log(res); //"Sucess"。
}).catch(error => {
 // Promiseが失敗した時に呼ばれる関数ブロック
 console.log(error)
})

 

.then()のメソッドチェーン

.then()は、以下のコード例のように繋げて書くこともできます。

then()が返すのもPromiseオブジェクトであるので、チェーンすることが可能です。then()の中でreturn <値>することは、resolve(<値>)と等しくなります。一方で、何もreturnしないときは、resolve()となるので、以下の例のようにundefinedとなります。

const customMadePromise = new Promise((resolve, reject) => {
 // aは常にtrueなので、このPromiseの処理は常に成功します。
 const a = true;
  
  if (a) {
   // 処理が成功したら、'Sucess'という文字列を返すと定義する。
   resolve('Sucess');
  } else {
   // 処理が失敗したら、'Failed'という文字列を返すと定義する。
   reject('Failed');
  }
}) 

// Promiseを処理。
customMadePromise.then(
  res => {
   console.log(res) // "Sucess"
   return "OK";
  }
).then(
	res => console.log(res) // "OK"。一つ前のチェーンで"OK"をリターンしているので。
).then(
	res => console.log(res) // undefined。一つ前のチェーンで何も返していないので。
)

 

次は、この処理をrejectさせてみましょう。

customeMagePromiseはrejectするので、then()をスキップして、.catch()に入ります。

const customMadePromise = new Promise((resolve, reject) => {
 // rejectさせます。
 const a = false;
  
  if (a) {
   // 処理が成功したら、'Sucess'という文字列を返すと定義する。
   resolve('Sucess');
  } else {
   // 処理が失敗したら、'Failed'という文字列を返すと定義する。
   reject('Failed');
  }
}) 

// Promiseを処理。
customMadePromise.then(
  res => {
   console.log(res) 
   return "OK";
  }
).then(
	res => console.log(res)
).then(
	res => console.log(res)
).catch(err => console.log(err)) //"Failed"

 

Promise.resolve の使い方

Promise.resolve は、new Promiseのショートハンドです。書き方がすっきりするだけで仕組みは同じです。ちなみに、このように仕組みは同じで書き方が違うようなことを「シンタックスシュガー」といいます。

以下のコードは、両方とも「”Hello”」という文字列をresolveに渡しています。

Promise.resolve('Hello').then((res) => console.log(res)); // Hello
new Promise((resolve) => resolve('Hello')).then((res) => console.log(res)); // Hello

以上をまとめると、Promise.resolveとは「渡した値でFulfilledされるpromiseオブジェクトを返すメソッド」となります。見落としがちなポイントとして、上記のコードは、共にPromise Objectをリターンしますので、コンソールにでもコピペして確認してみてください。

Promise.reject の使い方

さっき説明した、Promise.resolveのrejectバージョンがあります。

同様に、以下の二つのコード例は基本的に同じことをしていますが、書き方が違うだけです。

new Promise((resolve, reject) => {
    reject(new Error("エラー"));
}); // Uncaught (in promise) Error: エラー

 

Promise.reject(new Error("エラー")).catch((error) => {
    console.error(error); // Error: エラー
});

 

Promise.finally()の使い方

ECMAScript 2018(ES9)から追加された、新しいメソッドに、Promise.finally()というものがあります。

Promise.finally()は、成功時と失敗時のどちらのケースでも呼び出したい処理を書くのに最適です。一点注意点をあげると、 .then メソッドと違い、.finallyメソッドのコールバック関数は引数を受け取りません。下記コード例ないのコメントでも説明しています。

function onFinally() {
    // 成功、失敗どちらでも実行したい処理をかく
    // 'final'というストリングをリターンしてみます。
  return 'final'
}

// `Promise#finally` は新しいpromiseオブジェクトを返す
Promise.resolve(777)
    .finally(onFinally)
    .then((value) => {
        // 呼び出し元のpromiseオブジェクトの状態をそのまま引き継ぐので `777` で resolveされている.
      // then メソッドとは違い、.finally()メソッド内で値をリターンしてもPromise チェーンには影響なし。
     // `final`はリターンされない。
        console.log(value); // 777
    });

Promise.all()の使い方

基本的には、先ほどの.then()と同じですが、Promise.allの場合には、渡されたpromiseオブジェクトの配列が全てresolveされた時に、 新規のpromiseオブジェクトはその値でresolveされます。新規のpromiseオブジェクトとは、Promise.all()の返り値のことです。

// まずは、3つのPromiseを定義します。ご覧の通り、全てresolveします。
const promise1 = new Promise((resolve, reject) => {
	resolve('promise1 OK')
});

const promise2 = new Promise((resolve, reject) => {
	resolve('promise2 OK')
});

const promise3 = new Promise((resolve, reject) => {
	resolve('promise3 OK')
});


Promise.all([
promise1,
promise2,
promise3
]).then(res => console.log(res));// ["promise1 OK", "promise2 OK", "promise3 OK"]

 

次は、promise2をrejectされるように変更してみます。一つでもPromiseの値がrejectされた時点で、新たなpromiseオブジェクトはrejectされます。つまり、全ての処理が成功しないと、.then()のメソッドは実行されません。

const promise1 = new Promise((resolve, reject) => {
	resolve('promise1 OK')
});

// こちらのPromiseをrejectに変更します。
const promise2 = new Promise((resolve, reject) => {
	reject('promise2 Failed')
});

const promise3 = new Promise((resolve, reject) => {
	resolve('promise3 OK')
});


Promise.all([
promise1,
promise2,
promise3
]).then(res => console.log(res)).catch(err => console.log(err))//"promise2 Failed"

 

Promise.race()の使い方

渡されたpromiseオブジェクトの配列のうち、 一番最初にresolve または rejectされたpromiseにより、 新たなpromiseオブジェクトはその値でresolve または rejectされます。

以下の例では、promise1が最初にresolveされたので、その時点で新たなpromiseオブジェクトは、”promise1 OK”をresolveします。

const promise1 = new Promise((resolve, reject) => {
	resolve('promise1 OK')
});

const promise2 = new Promise((resolve, reject) => {
	resolve('promise2 OK')
});

const promise3 = new Promise((resolve, reject) => {
	resolve('promise3 OK')
});


// .all() を .race()に変更します。
Promise.race([
promise1,
promise2,
promise3
]).then(res => console.log(res)).catch(err => console.log(err))
// "promise1 OK"

まとめ

今日は、Promiseの基本を解説しました。同じ処理をやりたいのに、色々な書き方が存在するのが、初心者にとって混乱のポイントではないかと思います。やっている事は、そんなに複雑ではないので、今日のシンプルな例で一度整理してみると理解が進むと思います。

ABOUT ME
Wata
30歳で営業職からエンジニアに転職した者のブログです。 大手海運業→総合商社→ソフトウエアエンジン。現在は、オーストラリアにてエンジニアとして働いています。 未経験からのエンジニアへの転職、フロントエンド周りの技術、エンジニアの仕事環境、趣味の旅行、JAL修行、オーストラリアの情報などを発信しています!

COMMENT

メールアドレスが公開されることはありません。 が付いている欄は必須項目です