「JavaScript promiseはどうやって使うの?」
「JavaScript promiseでどんなことができるのか知りたい!」
「JavaScript promiseの注意点を知りたい!」
この記事に訪れたあなたはこのような悩みを持っていませんか?
promiseを初めてみたとき、どのように使われているのか全くわかりませんよね。調べてみても書かれている内容が難しく、途中で読むのをあきらめてしまった人もいるでしょう。
そこで、今回は初心者の人に向けJavaScript promiseの使い方や注意点を、サンプルコードも交えわかりやすく解説します。
具体的な内容は以下の通りです。
- JavaScript promiseとは?
- JavaScript promiseでできること
- asyncやawaitとは?
- JavaScript promiseを使った使用例
- JavaScript promiseの注意点
- JavaScript promiseを学習する方法
この記事を読めば、今までわからなかったpromisesの使い方を理解できますよ。
JavaScript promiseとは?
JavaScript promiseは非同期処理の操作が終わったときに、結果を返す機能です。非同期処理は、一連の処理において操作が終わる前に次の処理の操作をします。
JavaScriptではコードを上から順に読んで実行していくため、複数の操作を実行できません。そのため、非同期処理をしてくれるpromiseが用意されているわけなんですね。
なぜ非同期処理をしてくれる機能があるのでしょうか?
先ほど開設した通り、JavaScriptはpromiseがないと非同期処理ができません。そうなった場合、一連の処理を一つずつ実行していくため時間がかかってしまいます。
一方、非同期処理ならある処理を実行している間に別の処理を実行してくれるので、処理速度が上がるんですね。
またpromiseはAの処理が終わった後にBの処理が実行してくれるように、順番を決めて処理できるようになる役割もあります。
次にpromiseはどのように記述するか見てみましょう。
const sample = new Promise(function(resolve,reject){
resolve("success!");
reject("falied..");
});
上記のコードを見ると、promiseメソッドを作成してresolveとrejectを引数とする関数を作っていますね。
これがpromiseの基本形であり、中身の処理が成功すればresolveを、失敗すればrejectを返してくれます。
promiseによって実行された処理が完了するとき、実行した処理が正しく終わるときにresolveを、正しく終わらないときにrejectを返すと覚えておくといいでしょう。
JavaScript promiseでできること
promiseについて簡単に解説しましたが、これだけではpromiseを使うことはできません。非同期処理を効率よく行えるように以下のできることが用意されています。
「それぞれどのように使うのか?」を詳しく解説しますね。
thenでpromise実行後の処理ができる
最もpromiseと一緒に使われるのがthenでしょう。thenはpromiseで処理してから実行されるものです。promiseのコールバック処理を実行するとも言います。
どのように使われるのか以下のコードを見てみましょう。
const sample = new Promise(function(resolve,reject){
setTimeout(function() {
let one = 1;
resolve(one);
}, 1000);
});
sample.then(function(value){
console.log("success! - "+value);
},function(err){
console.log("falied.. "+err);
});
console.log("success! - 2");
// 実行結果
success! - 2
success! - 1
実行するとまずは変数sampleにpromiseを作成しています。次に作った変数sampleにthenでコールバック処理していますね。
ここでコールバック処理するときに、そのまま12行目のconsole.logを実行しているのがわかるでしょうか?
実行画面ではコードの12行目のconsole.logが先に実行されていて、そのあと8行目のconsole.logが実行されていますよね。
つまり以下の図のように、thenとconsole.logが並行して実行されていきます。
thenについてよく見ていきましょう。コールバック処理が始まると、変数sampleにあるsetTimeoutが実行されます。
その1秒後に変数oneに格納している1が、then以降の関数にvalueとして入りました。
promiseでresolveが返されると1つ目の関数が、rejectが返されると2つ目の関数が実行されますので、よく覚えておきましょう。
ちなみに2つ目の関数は省略できるので、基本的には書かなくて問題ありません。
allで複数のpromiseを実行する
thenで非同期処理できるようになると、「いくつもの関数を同時に並行処理したい!」と思いますよね。そんな時はallを使って複数のpromiseを実行しましょう。
const sample_A = new Promise(function(resolve,reject){
setTimeout(function() {
let one = 1;
resolve(one);
}, 1000);
});
const sample_B = new Promise(function(resolve,reject){
setTimeout(function() {
let two = 2;
resolve(two);
}, 500);
});
Promise.all([sample_A,sample_B]).then(function(value){
console.log("success! - "+value);
});
console.log("success! - 2");
// 実行結果
success! - 2
success! - 1,2
上記のコードを実行すると、しっかりと二つの関数が並行処理されているのがわかりましたね。
allで関数を実行したとき、次にthenが実行されるタイミングはallで選択したすべての関数が実行し終わったときです。
つまり処理の流れは以下のようになっているんですね。
またallで複数の関数を選択するときは、[]で括る必要がありますので注意しましょう。
catchでpromise中のエラーを出力できる
「エラーが出てしまう場合の処理を動かしたい!」と思ったとき、thenを使って2つ目の関数を作ればいいという解説はしました。
しかしコードがやや見づらくなってしまうので、もっと見やすい方法で記述したいですよね。
そこでcatchを使えばわかりやすいコードになりますよ。
const sample = new Promise(function(resolve,reject){
setTimeout(function() {
let one = 1;
reject(one);
}, 1000);
});
sample.then(function(value){
console.log("success! - "+value);
}).catch(function(err){
console.log("falied.. "+err);
});
console.log("success! - 2");
// 実行結果
success! - 2
falied.. 1
実行すると変数sampleにはreject()が返されるので、thenではcatchが実行されましたね。
catchの使い方はthenで2つ目の関数を作成したときと同じで、失敗したときの処理を記述して終わりです。
失敗したときの処理が見やすいため、reject()が返ってきたときの処理はcatchを使いましょう。
finallyで最後に行いたい処理ができる
promiseを書いていると、「処理が正常に実行できてもできなくても、最後にこの処理をさせたい!」と思うことがあるでしょう。
こういった操作はfinallyで実現できます。以下のコードを見てみましょう。
const sample = new Promise(function(resolve,reject){
setTimeout(function() {
let one = 1;
resolve(one);
}, 1000);
});
sample.then(function(value){
console.log("success! - "+value);
}).catch(function(err){
console.log("falied.. "+err);
}).finally(function(){
console.log("完了");
});
console.log("success! - 2");
// 実行結果
success! - 2
falied.. 1
完了
実行すると、最後にfinally()である完了という文字が出力されました。今度は4行目をreject()に変えて実行してみると、ちゃんと完了という文字が出力されていますね。
この方法はcatchの後にthenをつけることでも実現できます。しかし関数を2つ用意しないといけないので、コードが見づらくなってしまうんですね。
finallyもcatchと同様に、コードが見やすくなるためのものと考えておきましょう。
asyncやawaitとは?
promiseと似たような機能にasyncやawaitがあります。asyncはpromiseと似たような性質を持ち、awaitはpromiseの結果が返されるまで実行を待つ性質を持つんですよ。
そのため非同期処理が実現できますが、promiseよりも簡単に非同期処理が記述できるんですね。以下のコードを見てみましょう。
async function sample(){
let one = 1;
let two = 2;
console.log(await (one+two));
};
sample();
// 実行結果
3
asyncで非同期処理の関数を作成し、awaitで2つの変数が足されるまで出力を待っています。
6行目で関数を実行していますが、後ろにthenやcatchを付けることもできるんですね。コードも見やすくなるためにasyncで関数を作成する方法も覚えておくといいでしょう。
JavaScript promiseを使った使用例
promiseでできることを紹介しましたが、「いまいちよくわからない…」と感じる人もいるのではないでしょうか?
ここでは具体的にpromiseがどのように使われているのかを、サンプルコードを載せて紹介します。
コードを1行ずつ見て、仕組みがわかるようになってくださいね。
使用例1:関数aを実行した10秒後に関数bを実行させる
まずは関数aを実行して、10秒後に関数bを実行させてみましょう。10秒後に関数を実行させる方法はthenを使えばいいんでしたね。
作成したコードは以下のものになります。
function a(resolve,reject){
setTimeout(function(){resolve("success!")},10000);
}
function b(data){ console.log(data); }
const p = new Promise(a);
p.then(b);
// 実行結果
success!
実行すると、関数aにあるsuccess!が関数bで実行されました。
6行目を見ると変数pを作成してpromiseを作っていますね。中身は非同期関数を作成していましたが、この部分を変数にすることもできるので覚えておくといいでしょう。
そして変数pにthenをつけて10秒後に関数bが実行されるようにしているんですね。
使用例2:複数の関数を並列処理してから関数aを実行させる
次は複数の関数を並列処理してから関数aを実行させましょう。複数の処理を同時に処理するのでここではallが必要になりますね。
const b = new Promise(function(resolve,reject){
setTimeout(function() { resolve(10); }, 1000);
});
const c = new Promise(function(resolve,reject){
setTimeout(function() { resolve(20); }, 500);
});
const a = function(value){ console.log(value[0]+value[1]); };
Promise.all([b,c]).then(a);
// 実行結果
30
どのように動いているのかは「JavaScript promiseでできること」で解説したのとほとんど同じです。
まずPromise.allで関数を複数選択して、返ってきた結果に対してthenで関数aを実行しているんですね。
複数関数を選択したとき、返ってくる値は[10,20]のように配列として返ってきます。それぞれの値を使うときはfor文などで1個ずつ使うようにしましょう。
使用例3:5秒ごとに文字を出力させる
次は5秒ごとに文字を出力させてみましょう。
5秒間隔で文字を出力させるので、promiseによる非同期処理が必要になります。また繰り返し実行されるので無限ループも必要になりますね。
ここで注意してほしいのですが、「while文の中にthenで出力させれば行けそう!」と考えたあなたはよく考えてみてください。
thenが実行されるとpromiseから結果が返ってくる前にその先へと処理が進んでいってしまいます。
つまり、すべての繰り返しが5秒後にずれるだけで、5秒ごとに出力してくれないんですね。
そこで結果が返ってくるのを待つawaitを使います。以下のコードを見てみましょう。
async function time_p(){
let timeout = () => new Promise(resolve => setTimeout(resolve,5000));
while(true){
console.log(1);
await timeout();
}
}
time_p();
実行すると5秒ごとに1が出力されましたね。while文の中で1を出力し、awaitで5秒が経過するのを待っています。
この操作を繰り返すので5秒ごとに出力できるようになるわけなんですね。
JavaScript promiseの注意点
promiseはとても複雑なので、途中で表示がおかしくなったりエラーが出たりすると、わからなくなってしまいますよね。
「なんで上手くいかないんだろう…?」と疑問に思っている方は以下の注意点に気を付けてみましょう。
それぞれ解決方法を載せて解説しますね。
注意点1:Promiseに間違いはないか気を付ける
よくある間違いとして構文を間違えている可能性があります。以下のコードも構文エラーが出てしまいますが、どこがおかしいかわかりますか?
const sample = new Promise(function(x){
setTimeout(function() {
resolve();
}, 1000);
});
上記のコードのおかしいところは、関数の引数部分ですね。関数の引数にはxがありますが、中身を見るとxはなく、代わりにresolveが記述されています。
Promiseで作成した関数は、第一引数にresloveとなる変数を、第二引数にrejectとなる変数を入れる必要がありました。
そのため正常に動いたときに結果を返したいなら、第一引数の変数名と中身の変数名は一致していないといけないんですね。
よって上記のコードを修正するなら、引数のxをresolveにするか、中身にあるresolveをxに書き換える必要があります。
注意点2:catchが実行されたときの挙動に気を付ける
またpromiseにはもう一つ注意点があります。非同期処理してほしい一連の操作を記述するとき、catchを書くことがほとんどですよね。
しかしpromiseでrejectを返し、catchが実行される場合と実行された後はどのように処理が進むのかはわかりますか?
まずrejectでcatchが実行されるとき、rejectを返した時点から最初のcatchに移るまでにあるthenはすべてスルーされます。
図に表すと以下のように3つのthenはスルーされるわけなんですね。
またcatchを実行した後はかならずthenを実行します。これはcatchを通るとresolveとして返すようになっているからなんですね。
図に表すと以下のようになります。
このようにcatchを実行するときと実行された後の処理はすこし特殊なので覚えておくといいでしょう。
JavaScript promiseを学習する方法
ここまでpromiseの使い方を解説してきましたが、どのように学習すればいいのかわかりませんよね。とても複雑な機能なので「学習しても覚えられるかな…」と感じる人もいるでしょう。
そんなあなたは以下の方法で学習してみましょう。
- 本で学習・勉強する
- サイトで学習・勉強する
それぞれ学習するときのポイントを合わせて解説していきます。
本で学習・勉強する方法
JavaScriptの参考書なら、promiseや非同期処理について詳しい内容が書かれています。
またこの記事では触れませんでしたが、「functionって何だろう?」とコードについてわからない部分も参考書なら使い方も合わせて紹介されているんですね。
JavaScriptを一通り学習できるため、将来JavaScriptを使った職業に就きたいと考えている人はおすすめですよ。
ただし参考書の通りにサンプルコードを書いてもエラーを起こしてわからなくなってしまう事もあります。
わからないまま放置しないように、わからないことを教えてくれる人を見つけて不明点を無くしてくださいね。
おすすめの参考書は以下の記事で紹介していますので、気になった人はぜひ参考にしてみてください。
→ 【2021年最新】Javascriptおすすめ学習本・参考書13選!初心者から経験者まで
サイトで学習・勉強する
サイトでもpromiseを学習できますよ。参考書とは違い、あなたがわからない部分をすぐに解決してくれるメリットがあります。
例えば「asyncをもっと具体的に知りたい!」と思ったとき、ネットで調べるとasyncについて解説しているサイトが多数ヒットするんですね。
また参考書のようにJavaScriptを学べるサイトも存在します。中でも初心者におすすめしたいサイトが以下の3つ。
- 1. はじめてのJavaScript – ドットインストール
- 2. マンガで分かるJavaScriptプログラミング講座
- 3. 【初心者から創れる人へ】ProgateのHTML&CSSコースをやってみた!
これらのサイトは、環境構築から学べるのでこれからJavaScriptを触る人でも挫折せずに始められますよ。
無料で一通り学べてしまうのもサイトならではの魅力ですね。
まとめ
今回はJavaScript promiseについて紹介しました。
Promiseを使うことによって、「この処理をした後に、別の処理をしてほしい!」といった考えが実現できるようになります。
冒頭で解説した通り、JavaScriptは上から順番に実行されていくマークアップ言語です。
そのため、promiseを使わないで処理の順番を決めようとしたとき、関数の中に関数を多用することになりコードが非常に見づらくなってしまうんですね。
しかしpromiseなら1回の関数で済むことがほとんどで、とてもわかりやすいコードになります。実は、promiseはコードを見やすくするための機能として欠かせない存在なんですね。