Promiseの使い方

コールバック関数は、ネスト(入れ子)にすることができますが、複雑になります。このような時は、Promiseを使うと便利です。

本ページでは、Promiseについて説明します。

コールバック関数のネスト

関数を順番に呼び出したい時、コールバック関数をネストできます。

ネストされたコールバック関数

以下は、例です。

【コールバック関数のネスト例】
function func1(callback) {
  alert("func1完了");
  callback();
}
function func2(callback) {
  alert("func2完了");
  callback();
}
function func3() {
  alert("func3完了");
}

func1(function() {
  func2(function() {
    func1(function() {
      func2(func3);
      });
  });
});

上記はfunc1→func2→func1→func2→func3の順番で呼び出されますが、赤字の所がわかり辛いと思います。これに引数や変数などが加わってくると、記述していても間違いやすくなります。

このようにコールバック関数は、簡単に次の関数を指定できるメリットがありますが、ネストするとわかり辛いデメリットがあります。

Promiseで書き換えた場合

先ほどの例をPromiseを使って書き換えると、以下になります。

【Promiseの利用例】
function func1() {
  alert("func1完了");
}
function func2() {
  alert("func2完了");
}
function func3() {
  alert("func3完了");
}

Promise.resolve()
  .then(func1)
  .then(func2)
  .then(func1)
  .then(func2)
  .then(func3);

赤字が書き換えた部分ですが、実行される順番が非常にわかり易くなったと思います。

thenは、処理が完了するたびに呼び出される関数を指定します。

Promiseチェーン

Promiseでは、returnで返す値をthenで呼ばれる関数に渡せます。以下は、例です。

【Promiseチェーンの例】
function func1(x){
  alert(x);
  return "2番目";
}
function func2(x){
  alert(x);
}

Promise.resolve("1番目")
   .then(func1)
   .then(func2);

return "2番目"によって、func2には文字列"2番目"が渡されます。これにより、上記は「1番目」の次に「2番目」がアラートとして表示されます。

このように、値を次の関数に渡したりして処理が連結されることをPromiseチェーンと呼びます。

Promiseの状態

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

pending(保留)
処理中
fulfilled(満足)
処理が成功
rejected(拒否)
処理が失敗

Promiseチェーンで示した例で言えば、func1を呼び出した時点がpendingです。func1が終了した時点がfulfilledで、次のthenで指定した処理が始まり、再度pendingになります。

エグゼキューター関数

Promiseは、成功と失敗で処理を分けて記述できます。

【エグゼキューター関数の利用例】
function func1(resolve, reject){
  let x = Math.ceil( Math.random() * 2);
  if(x == 1){
    resolve("成功");
  } else {
    reject("失敗");
  }
}

function func2(z){
  alert(z);
}

const promise1 = new Promise(func1);
promise1
   .then(func2)
   .catch(func2);

func1は、エグゼキューター関数と呼ばれます。resolve()によって、Promiseの状態がfulfilledになり、thenで指定した関数が呼び出されます。reject()では、Promiseの状態がrejectedになり、catchで指定した関数が呼び出されます。

このため、上記はxが1の時は「成功」とアラート表示され、それ以外では「失敗」とアラート表示されます。

非同期処理との組み合わせ

Promiseは、resolveかrejectが返るまで、次の処理に移りません。このため、非同期処理が終わるまで待つことができます。以下は、非同期処理のsetTimeoutと組み合わせて使う例です。

【PromiseとsetTimeoutの組み合わせ例】
function func1(resolve, reject){
  setTimeout(function(){resolve("処理完了");}, 5000);
}

function func2(x){
  alert(x);
}

const promise1 = new Promise(func1);
promise1
   .then(func2);

上記は、プロミスの成功時だけを扱っています。func1が実行され、setTimeoutでは5秒待つことになります。5秒後にresolve();が実行されますが、これが成功を示します。成功を示す応答があると、次のthenでfunc2が処理されます。つまり、5秒後にfunc2が実行されて、「処理完了」のアラートが表示されます。

もし、Promiseを使わずにfunc1とfunc2を実行すると、5秒待たずにアラートが表示されます。また、func1の中でsetTimeoutの次にalert("処理完了");と記述しても、5秒待たずにアラートが表示されます。これは、setTimeoutの中の処理は5秒待ちますが、その下の処理は非同期で進むためです。

関連ページ

JavaScript基礎「非同期処理
非同期処理として、setTimeout、setInterval、async functionやawaitなどの使い方、setTimeout、setIntervalのキャンセル方法を説明しています。