🐨

JavaScriptのPromiseについて確認する

2023/11/30に公開

1.記事の目的

前回JSの同期処理と非同期処理について記事を書きました。
https://zenn.dev/tomokumo/articles/77e41863535b09

今回は非同期処理を同期させる方法について確認したいと思います。
現在主にJSではasync/awaitが利用されていますが、その理解を深めるためにPromiseの概念をまず確認します。

2.Promiseとは

1.Promiseの基本

まず、前回も記載した非同期処理になっているコードをPromiseを使用して同期的に書き直したいと思います。

前回のコード
function sayHello(name){
setTimeout(function(){ 
    console.log(`Hello! My name is ${name}.`);
  }, 1000);// 処理の実行に1秒挟む
}

function whatYourName(){
  console.log("What your name?");
}

sayHello("TARO");
whatYourName();
Promiseを使用したコード
function sayHello(name) {
  return new Promise(function(resolve) {
    setTimeout(function() {
      console.log(`Hello! My name is ${name}.`)
      resolve();
    }, 1000);
  });
}

function whatYourName() {
  console.log("What's your name?");
}

sayHello("TARO")
  .then(whatYourName);

Promiseを使用したコードでは、sayHello関数の返り値としてのnew Promiseの中に非同期処理を書いています。

Promiseはpending(初期状態)fulfilled(処理が完了して終了した状態)rejected(処理が失敗した状態の3つの状態を値(最終的にこの値がreturnされてきます)として持つオブジェクトです。
どのPromiseもまずはpending状態となります。非同期関数が処理完了するとfulfilled状態となります。(setTimeoutだと考えにくいですが、例えばfetch()などだとfetchに失敗してrejected状態が返される状態などが考えられるかと思います)

Promiseはコールバック関数を2つ引数に取ることができます。ここではresolverejectという名前とします。
resolveコールバック関数に非同期処理内で発生した処理の結果を格納したり、rejectedコールバック関数に、例えばErrorコンストラクターを格納してエラーオブジェクトを生成させたりします。(最低限一つの引数を取ることが必須です:ここではresolveと名前をつけたものです。rejectはなくても大丈夫です。特に結果を格納する必要がなければ、先ほどの例のようにresolve()のように引数がない状態もOKです。)
先ほどまでの例だとコールバック関数に受け取るものなどがないので、起きていることをよりわかりやすくするために下記にまた別のコードを用いて例を挙げたいと思います。

function addNumbers(a, b) {
  return new Promise(function(resolve, reject) {
    setTimeout(function() {
      if (typeof a !== 'number' || typeof b !== 'number') {
        reject(new Error("引数は数値である必要があります。"));
	// if文の条件が満たされるとreject関数が実行される
      } else {
        const result = a + b;
        console.log(`The sum of ${a} and ${b} is ${result}.`);
        resolve(result);
	//それ以外の場合にresolve関数が実行
      }
    }, 1000);
  });
}

function displayResult(result) {
  console.log(`The final result is ${result}.`);
}

function displayError(error) {
  console.log(`An error occurred: ${error.message}`);
}

addNumbers(3, 7)
  .then(displayResult) //resolve関数が実行されると、.thenメソッドが実行
  .catch(displayError); //reject関数が実行されると、.catchメソッドが実行

addNumbers("1", 7)
  .then(displayResult)
  .catch(displayError);

非同期処理内の処理をコールバック関数に格納してから、関数呼び出し部のメソッドで次の処理を行うようにすることで、非同期処理を同期的にできるのがPromiseの役割ということですね。
次にthenとcatchについても確認したいと思います。

2.thenメソッドとcatchメソッド

1.thenメソッド

thenメソッドはPromiseのメソッドであり、Promiseがfulfilled状態になった場合とrejected状態になった場合のコールバック関数を取ることができます。
上記のコードの関数呼び出し部分ですが、

addNumbers(3, 7)
  .then(displayResult) 
  .catch(displayError); 

addNumbers("1", 7)
  .then(displayResult)
  .catch(displayError);

下記のように書くことも可能です。

addNumbers(3, 7)
  .then(displayResult,displayError)  

addNumbers("1", 7)
  .then(displayResult,displayError) 

これをよりわかりやすくするために、catchメソッドを使います。

2.catchメソッド

catchメソッドはPromise内の処理がrejectted状態になったときに呼び出される関数です。

addNumbers(3, 7)
  .then(displayResult) 
  .catch(displayError); 

addNumbers("1", 7)
  .then(displayResult)
  .catch(displayError);

関数呼び出し部分はこのように書いた方が、より視覚的にわかりやすくなるかと思います。

4.記事のまとめ

Promiseの概念を応用したものがasync/awaitなのですが、思った以上に長くなってしまったので、次回に続きたいと思います。
(記事執筆後リンクを掲載します)

技術的な誤りに気がついた方はコメントいただけると嬉しいです。

5.参考記事

https://tcd-theme.com/2021/09/javascript-promise-then-catch.html
https://www.tohoho-web.com/ex/promise.html
https://www.sejuku.net/blog/52314
https://jsprimer.net/basic/async/
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Promise
https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Using_promises
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Promise/then
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Promise/catch

Discussion