業務で、node.jsを書く機会があり、勉強を始めて、理解に苦しんだ非同期処理
について書こうと思います。
#非同期処理とは
通常プログラムを実行する際には、上から処理が実行されます。これを同期的な処理と呼ばれます。では、非同期処理とはなんなのか。。これは、時間のかかる処理があった際に先に同期的な処理つまり上から、時間のかからない処理を実行し、時間のかかる処理を後回しにして処理をする事です。上記の通り、非同期処理は、キャッシュ処理や、ファイルの読み込み処理等の時間のかかる処理を非同期で処理していくのですが、その日同期処理では、実行フローを制御するためにCallback関数を必ず使う必要があります。
今回は、非同期処理を例を用いて説明した後に、callback関数でよく言われる「callback地獄」を防ぐ、Promiseコンストラクタについて書いていこうと思います。
#非同期処理の例
下記の簡単な処理を用いて非同期処理について説明します。
console.log("start");
setTimeout(function(){
console.log("hogehoge");
}
,3000);
console.log("end");
この上記の処理は非同期処理になります。ここでは、先にコンソール上にstart->endで出力されて、最後に、setTimeoutの処理が実行されました。ここで、setTimeoutについて軽く説明しておきます。
setTimeoutは、第一引数に関数、第二引数に何ミリ秒で実行するかを指定する数字を取ります。
setTimeoutは、非同期関数と呼ばれ、非同期処理を行う時に使われる関数です。
#非同期処理で呼ばれるCallback関数
では、なぜ非同期処理に、Callback関数が必要なのか下記の処理を用いて説明します。
var hoge;
setTimeout(function(){
hoge="hoge";
},3000)
console.log(hoge);
この上記の処理を実行するとundefinedになります。通常の同期的処理だと、上から実行されるので、変数hogeの宣言後、変数hogeに文字列hogeが格納されて、コンソール上にhogeが出力される流れとなります。ですが、今回は、setTimeoutという非同期関数により、非同期処理が行われているため、変数hogeはundefinedになりました。ここで、もう一度、この上記の処理の流れを確認します。
1.変数hogeが宣言
2.console.log(hoge)が実行
3.setTimeoutで3000ミリ秒後に、引数の関数内の処理である、hoge="hoge"が実行。
これを関数に包むと
var showhoge=function(){
var hoge;
setTimeout(function(){
hoge="hoge"
},3000)
console.log(hoge);
}
このようになります。これだと、関数を呼ぶタイミングで実行されるので、hogeが出力されます。
ただ、これだとただhogeが出力されるだけの関数になってしまいます。
ここで、Callback関数でやりたい事を確認します。
1.非同期処理を決まった順序で処理をしたい。
2.Callback関数を非同期処理をしている関数の引数で呼び出す事で、もっと自由に処理をしたいという点です。
ではこれを理解して頂いた上で次の処理を見てください。
var hellowithcallback=function(callback){
var hoge;
setTimeout(function(){
hoge="hoge";
callback(hoge); //コールバック関数に引数を渡して実行している。
console.log("hello" + hoge);
},3000);
}
var hellofuji=function(data){
hoge="fuji";
console.log(hoge);
}
上記の処理では、hellowithcallback関数が非同期処理です。ここにcallback関数をhellofujiで定義して、hellowithcallback関数内で呼び出す事で、出力では、hello fuji hello hogeとなりました。
つまり、コールバック関数に引数を渡す事で、新しく定義したcallback関数で決まった順序で制御を行う事ができるようになったという流れです。
このように非同期処理を使う場合は、Callback関数を使い実行フローを制御をする事ができます。
このCallback関数が多ければ多いほど、非常に色々な処理ができますね。そんな時、気をつけなければいけない事が「コードの可読性」です。Callback関数のcallback関数が呼び出されて、またコールバック関数が呼び出されるといった読みづらいコードとなります。
上記の非同期的書き方は関数を入れ子にした書き方もできるわけです。非常に見辛い・・
ここでそんな入れ子の見辛い非同期処理を同期的に、つまり見やすく書く事ができるPromiseコンストラクタについて説明します。
#Promiseコンストラクタ
Promiseコンストラクタは非同期的な処理を同期的に書く事ができる関数です。では上記の最後に書いた、非同期処理をPromiseコンストラクタで書いてみます。
var hellowithcallback=new Promise(function(resolve,reject){
var hoge;
setTimeout(function(){
hoge="hoge";
console.log("hello"+hoge);
},3000);
resolve(hoge);
})
hellowithcallback.then(function(result){
result="fuji";
console.log("hello"+result);
})
上記のコードでは、まず非同期処理の関数をPromiseコンストラクタのインスタンスにして、処理をnewの引数に渡します。その際にPromise側で呼び出されるresolve,rejectというのは、resolveが処理が無事に完了した時の(成功した時の)戻り値を受け取る関数です。
rejectが処理が失敗した時の戻り値を受け取る関数です。Promiseコンストラクタのインスタンスするメリットは、非同期処理関数(Promiseコンストラクタ).then(コールバック関数)で実行できる事です。
このコールバック関数のコールバック関数、そのコールバック関数のコールバック関数という形で、どんどん入れ後になっていく形を
.then(コールバック関数)
.then(コールバック関数)
.then(コールバック関数)
と書く事ができます。
参考記事
→https://qiita.com/LightSpeedC/items/7980a6e790d6cb2d6dad
→https://qiita.com/koki_cheese/items/c559da338a3d307c9d88
→https://qiita.com/matsuby/items/3f635943f25e520b7c20
とりあえず今のところの理解です。。。
まだまだ勉強する必要がある。。