なんで?
そもそもNodeの環境は非同期型だという認識が甘くてすこしはまったのでメモ書きしておきます。
Node は非同期型の環境
Node は非同期の環境であり、他の言語とは大きく違います。
時間がかる処理Aが存在するとします。同期型の環境ではAが終わるのを待ってから次のBを実行します。それに対して非同期型の環境ではAをスキップしてBを実行してしまうのです。これは以下の簡単な実験で試すことができます。
Python(同期型の環境)
import time
def sayHello():
time.sleep(2)
print("hello")
sayHello()
print("hello2")
Node(非同期型の環境)
function sayHello(){
setTimeout(function(){
console.log("hello");
},2000);
}
sayHello();
console.log("hello2");
sayHello関数が時間のかかる処理に当たります。
PythonではsayHello()処理が完了するまで2秒待った後に次の処理が実行されます。
2秒待つ > hello出力 > hello2出力
Nodeでは、sayHello()処理が一度呼ばれるものの、即座に次の処理が実行されます。
hello2出力 > 2秒経つ > hello出力
JavaやPHPやPythonなど多くの言語は同期型のようです。Nodeは非同期型です。
非同期型でウェブ開発する上での問題点
非同期型でWeb開発していると困ってしまうことがあります。それは、遅延処理があった場合、処理が終わる前に画面レンダリングされてしまう、ということです。他のAPI から値を取得するために遅延する場合、DBからデータを取得するための遅延する場合などが該当します。
const Koa = require('koa');
const app = new Koa();
app.use(async ctx => {
setTimeout(()=>{
ctx.body="hello"
},2000);
});
app.listen(3000);
// ブラウザの画面にはNot Found と表示された(helloとは表示されない)
Promiseで同期処理
Promise はこの問題を解決してくれます。Promise は処理が終わるのを待って、終わり次第結果を返してくれるオブジェクトです。処理が成功したときにはresolve()、失敗したときにはreject()を呼びます。Promiseオブジェクトはthen()で発火できます。
const delayHello = new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve("hello!");
},2000);
});
delayHello.then((value)=>{
console.log(value);
})
// 2秒待ってからhello と出力される
Koa でPromise をうまく使うとこんな感じになります!
const Koa = require('koa');
const app = new Koa();
const sayHello = new Promise((resolve,reject)=>{
setTimeout(()=>resolve("hello!"),5000);
})
app.use(async ctx => {
sayHello.then((value)=>{
ctx.body = value;
);
// もしくはawait でもっと綺麗にかける
// const value = await sayHello;
// ctx.body = value;
});
app.listen(3000);
リポジトリ置いとくのでよかったらどうぞ!
https://github.com/yoneda/koa-templete/