Tech Racho エンジニアの「?」を「!」に。
  • Ruby / Rails以外の開発一般

JavaScript: 5分でわかるPromiseの基礎(翻訳)

概要

原著者の許諾を得て翻訳・公開いたします。


更新情報:

  • 2017/11/16: 初版公開
  • 2021/03/05: 更新

JavaScript: 5分でわかるPromiseの基礎(翻訳)

JavaScriptのPromiseをわかりやすく解説しました。Promiseの基本を5分で学びましょう。

この記事で学べること

本チュートリアルでは、JavaScriptの「Promise」の基礎を学びます。Promiseのすべてを網羅的に説明するものではありませんが、Promiseを理解してコードで使い始めるのに必要な知識を固めることができます。

Promiseが必要になるとき

Promiseを使うと、(コールバックのように)特定のコードの実行完了を待ってから次のコード片を実行できるようになります。

Promiseが重要な理由は何でしょうか。あるWebサイトがAPIからデータを読み込んで処理し、データを整形してからユーザーに表示するところを考えてみましょう。APIから情報を取得する前にデータの処理と整形を行おうとすれば、エラーか空白ページのどちらかが表示されておしまいです。Promiseを使うと、API呼び出しが成功するまでAPIデータが処理されたり整形されたりしないようにできます。

Promiseとは何か

JavaScriptのPromiseは、同期操作の最終的な結果を表す、一種のプレースホルダと考えてください。本質的にこのプレースホルダは、コールバックをアタッチできる1つのオブジェクトです。

Promiseのステートは、以下の3つのいずれかになります。

pending
非同期操作が完了していない
fulfilled
操作が完了し、Promiseが値を1つ持つ
rejected 
操作がエラーまたは失敗で終了した

pending状態でないPromiseは安定しています。いったん安定したPromiseのステートはずっとそのままになり、他のステートに移行することはできません。

Promiseを使う

Promiseを使う場合、関数から返された既成のPromiseを使うことがほとんどですが、関数のコンストラクタからPromiseを作ることもできます。

シンプルなPromiseは以下のような感じになります。

runFunction().then(successFunc, failureFunc);

上のコード例では、最初にrunFunction()を実行しています。runFunction()はPromiseを1つ返すので、Promiseが安定した場合にのみsuccessFunc関数かfailureFunc関数のいずれかを実行できるようになります。PromiseがfulfilledになるとsucessFunc関数が呼ばれ、Promiseが失敗するとfailureFunc関数が呼ばれます。

Promiseの例

次は独自のPromiseを作成するコード例です。読んですぐわからなくても問題ありません。

function delay(t){
  return new Promise(function(resolve){
    return setTimeout(resolve, t)
  });
}
function logHi(){
  console.log('hi');
}
delay(2000).then(logHi);

このコードにはdelay()関数とlogHi()関数という2つの関数があります。logHi()関数は単にコンソールに'hi'と出力します。delay()関数はもう少し複雑で、指定のタイムフレームを経過した後でresolve(解決)するPromiseを1つ返します。

then()メソッドを使って、最終的にfulfilledまたはrejectedのどちらかの値を受け取るコールバックを登録します。

以上を念頭に置いてdelay(2000).then(logHi)を呼び、2000ms(=2秒)という値をdelay関数に渡します。2秒経過するとPromiseがresolveし、その場合に限ってlogHi関数が呼ばれます。

Google Chrome Developer Toolsを開いてこのコード例を入力することでお試しいただけます。

Promiseのチェイン

Promiseの主なメリットのひとつは、いくつもの非同期操作をチェインできることです。つまり、最初の操作が成功した場合に限って次の操作を開始するように指定できるということです。これをPromiseチェインと呼びます。以下の例をご覧ください。

new Promise(function(resolve, reject) {
  setTimeout(() => resolve(1), 2000);

}).then((result) => {
  alert(result);
  return result + 2;
}).then((result) => {
  alert(result);
  return result + 2;
}).then((result) => {
  alert(result);
  return result + 2;
});

最初のPromiseは、2000ms経過するとresolveして値1を返します。resolve後はthen()ハンドラが呼び出され、アラートボックスに値1が表示されます。最後に値が2に足され、新しい値3が返されます。この値が次のthen()ハンドラに渡され、この処理を繰り返します。

上は現実のコード例ではありませんが、Promiseが互いにチェインする様子がわかります。Promiseのチェインは、JavaScriptで外部リソースを読み込む場合や、処理前にAPIデータの到着を待つといった特定のタスクで非常に有用です。

エラーハンドリング

ここまでに扱ったPromiseは「resolved」になるものばかりでしたが、ここからは趣向を変えます。.catch()を使うと、Promiseチェインのエラーをすべてキャッチできます。次のコード例で.catch()の動作をご覧ください。

// ....
})
.catch((e) => {
  console.log('error: ', e)
}

上は.catch()のシンプルなコード例です。これは、返されたエラーメッセージをコンソールにログ出力します。先のコード例にエラーハンドリングを追加してみましょう。

以下のコード例は、先ほどのものと2箇所しか違っていません。2番目の.then()の後ろにエラーとエラーメッセージを追加しました。また、チェインの末尾に.catch()も追加してあります。このコードを実行するとどうなるでしょうか。

new Promise(function(resolve, reject) {
  setTimeout(() => resolve(1), 2000);

}).then((result) => {
  alert(result);
  return result + 2;
}).then((result) => {
  throw new Error('FAILED HERE');
  alert(result);
  return result + 2;
}).then((result) => {
  alert(result);
  return result + 2;
}).catch((e) => {
  console.log('error: ', e)
});

結果は次のとおりです。

  • 2秒経過するとPromiseが値1でresolveする
  • この値が最初の.thenに渡されてアラートダイアログが画面に表示される。2が足され、新しい値3が2番目の.then()に渡される。
  • 新しいErrorがスローされる。実行は直ちに停止してPromiseはrejectedステートでresolveする。
  • .catch()はエラー値を受け取って画面にログを出力する。コンソールには次のように表示されます。

最後に

お読みいただきありがとうございました。Promiseについて詳しくは以下をご覧ください。

本記事がPromise導入のよいきっかけとなればと願っています。最終的にWeb開発を学ぶ用意のある方は、「6か月でフルスタックWeb開発を学ぶ究極ガイド」をぜひチェックしてみてください。

私はWeb開発記事を週に4本公開しています。毎週配信しているメーリングリストを購読したい方はフォームから登録いただくか、Twitterで私をフォローして下さい。

本記事が皆さまのお役に立ちましたら、元記事の下にある[👏]ボタンを数回クリックしてサポートをお願いします。

関連記事

JavaScriptの正規表現のコンセプトを理解する(翻訳)

JavaScriptスタイルガイド 1〜8: 型、参照、オブジェクト、配列、関数ほか (翻訳)


CONTACT

TechRachoでは、パートナーシップをご検討いただける方からの
ご連絡をお待ちしております。ぜひお気軽にご意見・ご相談ください。