1. HOME
  2. ブログ
  3. IT技術
  4. 【JavaScript】Promise で非同期処理を記述する

【JavaScript】Promise で非同期処理を記述する

はじめに〜同期処理と非同期処理について〜

本記事では JavaScript の非同期処理を扱うための Promiseという仕組みについて取り上げます。

JavaScript、とくにサーバーサイドで利用されるNode.js では非同期処理を利用するAPIが多数出てきます。

Promise の仕組みを理解しておくとプログラムの作成がはかどるはずです。

まず本題へ入る前に『同期処理』『非同期処理』について説明させていただきます。

JavaScript の同期処理

同期処理とは上から順番に処理されていくことです。

処理が終了するのを待ってから、次の処理を実行します。

[結果]
1
2
3

JavaScript の非同期処理

非同期処理とは、1つの処理が終了するのを待たずに、次の処理を実行することです。

終了を待たないので処理を終了する順番が、上下することがあります。

ファイルの読み込み、ネットワークの通信処理、データベースアクセスなどは非同期処理で行うことが多いです。

なぜ非同期処理は必要なの?

同期処理は上から順番に実行してくれますし、感覚的にもわかりやすいです。

ですが、同期処理だと時間のかかってしまう処理が終了するまでは次の処理を行うことができません。

たとえばサーバーと通信するとき、サーバーから応答が帰ってくるのに数秒かかる時があります。

その間、処理が止まっていては問題です。

その対策として、非同期処理があります。

非同期処理を用いた例

非同期関数のsetTimeout(function callback() {} , delay)は指定時間(ms) だけ処理を遅らせてから、callback 関数を実行します。

今回は callback 関数に console.log(2) を指定しました

[結果]
1
3
2

console.log(2) が呼ばれる前に、console.log(3)が呼ばれます。

setTimeout()は非同期関数のため、処理が止まることもなく続行されたためです。

Promise とは

前置きが長くなりましたが、ここからが本題です。

Promise とは、非同期処理の状態をあわらすオブジェクトです。

Promise の構文は以下のようなものです。

Promise 関数の対応状況

Chrome, Firefox, Edge, Safariといった最新のブラウザでは標準で対応しています。

IE 11 は対応していないので、 polyfill で対応する必要があります。

こんな方にオススメの記事

  1. JavaScript を学習中の方
  2. 非同期処理、同期処理がいまいちわからない方
  3. Promise を理解したい方

Promise の利用

Promise を利用する場合、関数の returnnew Promise() を指定し、コールバック関数を Promise に登録します。

コールバック関数には、処理が成功したときには resolveを呼び、処理が失敗したときは rejectを呼ぶように記述します。

注目してほしいのは以下の箇所です。

関数の returnnew Promise(function()) と記述しています。Promise インスタンスを返却するように記述すると、Promiseが利用できます

たとえば、購入処理に約0.5秒かかってしまうbuy()関数なんてものを作ろうと思ったら、このような記述になります。

お金が足りていたら、お釣りの金額を引数としたresolve(/*お釣り*/)を呼び、お金が足りていなければreject(/*メッセージ*/)を呼ぶ関数としています。

Promise の状態

さて、さきほどPromise とは状態を表すオブジェクトといいました。

具体的には以下の状態があります。

pending:初期状態、実行中。成功も失敗もしていない。
fulfilled:処理が成功した状態。
rejected:処理が失敗した状態。

  1. resolve() が呼ばれると、fulfilledの状態になります。
  2. rejected() が呼ばれると、rejectedの状態になります。

Promise の then()

Promise の処理が終了したのち、結果を取得するにはthen()を利用します。

then() は、 Promise のインスタンスの状態がfulfilled となったときに実行する関数を登録できるインスタンスメソッドです。

以下のような構文となっています。

onFulfilled : fulfilled の状態のとき(resolveが呼ばれたとき)に実行される関数
onRejected : rejected の状態のとき(rejectが呼ばれたとき)に実行される関数

resolve(/*引数*/) の引数部分が、onFulfilledonRejected のところで登録する関数の引数となります。

たとえば先程のbuy関数のお釣りを引数として受け取るには、以下のようにします。

[結果]
1
3
100円の商品を購入しました
お釣りは200円です

実行結果を確認すると、「お釣りは 200円です」と表示されており、resolve()の引数を受け取れていることが確認できるとわかります。

さて、さらにここで注目していただきたいのは順番です。

ソースコードの記述の順番と実行結果のメッセージの順番が異なっていることです。

[結果]
1
3
100円の商品を購入しました #結果は3番目
お釣りは200円です

buyは、 Promiseを利用して非同期関数となっており、処理が終了するまでそこでコードの実行が止まることはないからです。

console.log(1), console.log(3) は即時に実行されるますが、buy(300)の処理は約 0.5秒かかってしまうので、実行結果のような表示になります。

Promise の catch()

Promise のインスタンスのエラー処理には catch()を利用します。

catch() とは Promise のインスタンスの状態がrejected となったときに実行する関数を登録するインスタンスメソッドです。

それではあえて処理を失敗させて reject() を発生してみましょう。

buy()の引数に 100以下を指定します。(簡略化のためにアロー関数を利用しています)

[結果]
お金が足りないよ

then()が呼び出されずにcatch()だけが呼び出されてエラー処理されているのがわかります。

このように、Promise を利用したコードは then()catch()で正常処理、エラー処理を切り替えることができます。

Promise のメソッドチェーン

このthen(),catch() はつなげて利用することもできます。

つなげて利用する場合、Promise が終了するのを待ってから、次のthen() が呼ばれます。

それでは、連続で商品を購入してみましょう。

[結果]
100円の商品を購入しました
お釣りは450円です
100円の商品を購入しました
お釣りは350円です
100円の商品を購入しました
お釣りは250円です
100円の商品を購入しました
お釣りは150円です
100円の商品を購入しました
お釣りは50円です
お金が足りないよ

実行してみると約 0.5 秒間隔で文字が表示されていくと思います。

前述しましたが、このようにPromise のthen()を利用することで、連続して非同期処理を記述できます。

また、ソースコード中では buy()8回も呼んでいますが、購入処理が行われたのは 5回だけです。

お金が足りなくなったので途中の buy() の中でreject() が呼び出されたからです。

それ以降の then() の処理が行われることはなく、catch()でエラー処理されるからです。

then () のなかの関数の return の値が Promise インスタンスじゃなかったときは?

then() の引数として渡された関数がPromise を返すのではありません。

値を返した場合は、Promise.resoleve(<関数が返した値>)が自動的に実行され Promise でラップされます。

.catch() のあとの .then()

Promise のメソッドチェーンで .catch()のあとに.then()でチェーンすることも可能です。

これによってエラー処理が失敗したあとでも新しい動作を定義できます

フローチャート

すこし複雑ですが、上記のようなメソッドチェーンのフローチャートは以下の図のようになります。

実線が正常処理の流れ、点線が失敗時の流れです。

図を見ると、asyncRecovery1 でエラー処理を行った後に、正常処理のasyncThing4 に戻るフローがあるとわかります。

Promise で非同期処理の処理を包む

Promise は便利ですが、古い API は Promiseのインターフェイスを提供しておらず、callback 関数のみの時があります。

具体的な例としては setTimeout() が当てはまります。

setTimeout() を Promise に対応させてみましょう。

呼び出し方法

このように Promise で APIをラップしたのち、それ以降は二度と直接呼ばないようにするとよいです。

まとめ

以上、非同期処理の Promise についてでした。

最後に、まとめて終わりにしたいと思います。

  1. Promise を利用することによって非同期処理を記述できます。
  2. 正常時にはresolve() を呼び、エラー時にはreject()を呼びます。
  3. then() で処理をつなげることができます。
  4. catch() でエラー処理を記述できます。

Promiseの仕組みを理解しておくとプログラムの作成がはかどるので、ぜひ使いこなせるように勉強しましょう!

参考サイト

  1. Promise – JavaScript | MDN
  2. Promise を使う – JavaScript | MDN
  3. JavaScript Promise の本
  4. JavaScript の Promise: 概要
  5. JavaScript の同期、非同期、コールバック、プロミス辺りを整理してみる
  6. 今更だけど Promise 入門

ライトコードよりお知らせ

にゃんこ師匠にゃんこ師匠
システム開発のご相談やご依頼はこちら
ミツオカミツオカ
ライトコードの採用募集はこちら
にゃんこ師匠にゃんこ師匠
社長と一杯飲みながらお話してみたい方はこちら
ミツオカミツオカ
フリーランスエンジニア様の募集はこちら
にゃんこ師匠にゃんこ師匠
その他、お問い合わせはこちら
ミツオカミツオカ
   
お気軽にお問い合わせください!せっかくなので、別の記事もぜひ読んでいって下さいね!

一緒に働いてくれる仲間を募集しております!

ライトコードでは、仲間を募集しております!

当社のモットーは「好きなことを仕事にするエンジニア集団」「エンジニアによるエンジニアのための会社」。エンジニアであるあなたの「やってみたいこと」を全力で応援する会社です。

また、ライトコードは現在、急成長中!だからこそ、あなたにお任せしたいやりがいのあるお仕事は沢山あります。「コアメンバー」として活躍してくれる、あなたからのご応募をお待ちしております!

なお、ご応募の前に、「話しだけ聞いてみたい」「社内の雰囲気を知りたい」という方はこちらをご覧ください。

ライトコードでは一緒に働いていただける方を募集しております!

採用情報はこちら

関連記事