JavaScriptはシングルスレッドで並列処理ができないものの、非同期で実行されるため思わぬ挙動を示すことがあります。非同期で実行される、というのは要するにソースコードの上から順番に実行されるとは限らないということです。
しかし、例えばファイルを読み込んでから、そのファイルの中身を操作するなど、処理の順番が重要になってくる局面も数多くあることかと思います。そのような場合に、コールバック関数を活用することは手段のひとつですが、いわゆるコールバック地獄とよばれるネストの闇にはまってしまいます。
そこで、非同期処理の順番をコントロールするために非常に有用なPromiseの使い方について紹介していきます。
目次
非同期処理とは
そもそも非同期処理とはどのようなものでしょうか。イメージをつかみやすくするために、下にサンプルコードを用意しました(※Chromeの開発者ツールなどにコピペして実行してみてください)
See the Pen
NWKzQmX by Keita (@tfpiyfit)
on CodePen.
こちらの意図としては、setTimeoutで1秒後に”Hello”を表示した後、”World”と出したいと考えていました。しかし実際に実行するとHello worldではなくworld Helloになってしまいます。これはなぜでしょうか?
その理由は、setTimeoutが非同期型の関数であるからです。非同期とは上にも書いたように、単純にソースコードの上から実行されていくと約束されていないということです。上記のように、JavaScriptではsetTimeoutの実行が完了する前に、先にconsole.log(‘world’)が実行されるということが起こりうるのです。
では、こちらの意図どおりにHello worldと表示するにはどうすればいいのでしょうか?そこで役に立つのがPromiseです。
Promiseとは
PromiseとはJavaScriptに標準で備わっているオブジェクトです。非同期処理が失敗(reject)したか完了(resolve)したか、まだ実行されていない(pending)の状態をもつことができるので、この状態を読み取って非同期処理の順番を処理できます。
Promiseの使い方
さて、では実際のコードを見てみましょう。
See the Pen
NWKzQZy by Keita (@tfpiyfit)
on CodePen.
JavaScriptでは関数が第一級オブジェクトなので、変数や定数に渡すことができます。上記のコードでは、まずpromiseという定数に対してPromiseオブジェクトを入れます。
コンストラクタとなりnew Promise()の()の中には、先に実行したい関数を入れます。JavaScriptでは無名関数をよく使いますが、略すこともできます。
new Promise(function(resolved, rejected){ //処理 }); //こちらでも可 new Promise((resolved, rejected) => { //処理 });
1番目に行いたい処理は、1秒後に’Hello’を表示するというものなので、setTimeoutを使いますが、console.log(‘Hello’)が実行されたあとにメソッドresolved()を実行します。
これによって、オブジェクトpromiseの状態をresolved(実行済)とすることができます。そして、promiseがresolvedになってから実行する場合は、メソッドのthenを利用します。
処理の順番をまとめると以下の通りです。
- promiseオブジェクト生成によってコンストラクタが実行される
- console.log(‘Hello’)される
- promiseオブジェクトの状態がresolved(実行済)になる
- thenがpromiseオブジェクトのresolvedを検知して
- console.log(‘world’)を実行する
thenをつなげて書くこともできる
const promise = new Promise(function(resolve, reject){ setTimeout(function(){ console.log('Hello'); resolve(); }, 1000); }); promise.then(function(){console.log('world');}).then(function(){console.log('!');});
また、thenをつなげて書くことで順番に実行していくこともできます。これはPromiseを使う大きなメリットのひとつになります。コールバック関数を何重にもするとネストが深くなるコールバック地獄に陥ってしまいますが、thenをつかってメソッドをつなげることで非常にシンプルに表現することができるのです。
まとめ
JavaScriptではAjaxなど非同期処理がメリットになる一方で、扱いが難しいという点もあります。これをうまくコントロールできると、JavaScriptでできる範囲が増えるので、これからもっと勉強していかなければと思います。
実はPromiseに加えてasync/awaitという機能を使うことで、よりコードをシンプルに書いていくことが出来ます。続きは下記の記事をご覧ください。