現状
- 別に(今は)ふわっとした知識でいいのでpromiseを扱いたい
- node.jsでなんか色々やろうとしている
- プログラミングはほとんど経験がない(printfはできる)
- javascriptももちろん全く触ったことがない(最近はじめてnode.jsをいれてみた。なぜかいきなりnode.jsか〜)
- そもそもweb系の経験もない
- でもweb系をやりたいわけでもなく、サーバーもフロントも今後いじる予定は(今の所そんなに)ない
- 間違ってたら絶賛教えてほしい
ちなみに、同期処理と逐次処理を混同しているけどその点については今回は触れません。
promiseってなんぞ
- 非同期処理を扱いやすくするなんだかの仕様らしい
そもそも、非同期処理というがソースは上から順番に実行されると思っている私にとって非同期処理と言われてもまったくもってわからない!
非同期処理ってなんぞ...
同期処理
- 同期処理は名前の通り同期した処理
同期処理1();
同期処理2();
同期処理3();
- 処理1が終わって結果が返ってきたら処理2を行う
- 処理2が終わって結果が返ってきたら処理3を行う
私のような初学者のほとんどはこの処理が当たり前だと思っている、はず。
ちなみに、asyncが非同期処理でsyncが同期処理っぽいです。
非同期処理
- みんなてんでバラバラに実行されていく(てんでバラバラっていうのは嘘ですけど)
非同期処理1();
非同期処理2();
非同期処理3();
- 非同期処理1がスタートする
- 非同期処理2がスタートする
- 非同期処理3がスタートする
- 処理の終わった方からreturnしてくる
なのでjavaとかCとかちょろっと触ったことあるし、というノリでいきなりnode.jsやろうとすると死にます。死んだ。
私が一番実感したのは次のようなコードを書いたときです。
console.log('myFunc1の実行を開始======');
var buf = myFunc1();
console.log('myFunc1の結果を表示:', buf);
console.log('myFunc2の実行を開始======');
var buf2 = myFunc2();
console.log('myFunc2の結果を表示:', buf2);
console.log('myFunc3の実行を開始======');
var buf3 = myFunc3();
console.log('myFunc3の結果を表示:', buf3);
なお、myFunc1,myFunc2,myFunc3の中にはリターン時にconsole.log('myFunc1()の処理が終わりました!')
と返すようにしておく。あくまでも一例。
上記のようなコードをnode myFunc.js
とかで実行すると次のような結果が出力される。
myFunc1の実行を開始======
myFunc1の結果を表示: undefined
myFunc2の実行を開始======
myFunc2の結果を表示: undefined
myFunc3の実行を開始======
myFunc3の結果を表示: undefined
myFunc2()の処理が終わりました!
myFunc3()の処理が終わりました!
myFunc1()の処理が終わりました!
ここで私のような一般人は?????????と思うわけです。
想定しているのは次のような出力だったはず。
myFunc1の実行を開始======
myFunc1()の処理が終わりました!
myFunc1の結果を表示: 'foo'
myFunc2の実行を開始======
myFunc2()の処理が終わりました!
myFunc2の結果を表示: 'bar'
...
- この現象がまさに非同期処理(だと思っている)
- 上から順番に実行していくが、処理が終わったものから順次結果を返していく
そういうことが起きるものですから、何も知らない状態でmyFunc1の実行結果を受け取ってmyFunc2で処理しよう、などと考えると痛い目にあうわけです。
Promiseというもの
promiseという形に沿ったなにかをreturnする関数myPromise()というものがあったとする。
myPromise().then()
ふわっとした理解でいうならば、myPromiseの実行結果を受け取り.then()で処理してくれるらしい。
なんかこれを使えば逐次処理もうまくいきそうな気がしてきた。
なお、promiseにはよく入門サイトや解説サイトで次の使い方が紹介されている。
- .then()
- .then().then().then() (メソッドチェーン)
- Promise.all()
一応理解のために全部使ってみる。
Promiseを使ってみる
とりあえずpromiseという形の何かをreturnする関数myPromiseを作って、thenで逐次処理を目指す。
promiseのインスタンス化
function myPromise(){
return new Promise();
}
これでpromise型の何かを返すmyPromiseができたので、実際何を返してほしいのかnewしたPromise内部に記載していく。
- よく例にあがるsetTimeoutを利用して、3000ms(3s)後に文字列を返すような処理を書く
- setTimeOut()は次のような使い方をする。
setTimeOut(処理内容, 実行のタイミング(ms));
これをpromiseで包むが、次の点に注意が必要。
- promise型はresolve, rejectという値を返す
- resolveは処理が成功したとき
- rejectは処理が失敗したときに返される
なので、setTimeoutをpromiseで包むのは次のようにする。
function myPromise(){
return new Promise(function(resolve,reject){
setTimeout(処理内容, 実行のタイミング);
});
}
この時点で既にややこしすぎて心が折れそう。
だから3秒たったらHello myPromise!
と返してくる処理をpromise化すると、次のようになる。
function myPromise(){
return new Promise(function(resolve,reject){
setTimeout(function(){
// resolve(返したい値)
resolve('Hello myPromise!');
}, 3000)
});
}
.then
上記のmyPromise.jsで、とりあえずHello myPromise!
という文字列が返ってくるわけだが、この返ってきた値を受け取って表示したい。(逐次処理)
そういうときに、.thenを使う。
myPromise().then(処理内容);
したがって、myPromise内で行った処理を受け取ったら表示する、という処理を書くのはthen内。
myPromise().then(function(value){
console.log(value);
});
なお、thenにはcatchもできるようです。
myPromise().then(略).catch(function(error){エラー時の処理内容});
- catchじゃなくてthen内でも処理内容を書くこともできるらしい。それについては一身上の都合により略
myPromise().then(resolve時の処理内容, reject時の処理内容);
.thenを使ったメソッドチェーン
- thenを多用することで逐次処理が行える
- 例えば処理1を行った結果を用いて処理2、その結果を用いて処理3を行いたいとき
イメージとしてはこう。
myPromise(処理1).then(処理2).then(処理3)
注意すべきなのは、下記の点。
- 1つ目のthen(処理1)の返す値を使って2つ目のthen(処理3)を行う場合、1つ目のthen(処理2)の中身もpromise化しておかないと2つ目のthen(処理3)は勝手に処理を始めてしまったりする
ここらで流石にpromiseとthenの使い方を覚え始めた。
Promise.all()
Promise.all()を使うと、複数処理をまとめて走らせておいて全部終わったら値を受け取るという高度なことが簡単にできるらしい!
Promise.all([
promise化した処理1(),
promise化した処理2(),
promise化した処理3()
])
.then(function(data){
console.log(data);
})
- thenの処理内容は、処理1~3が全て終わってから実行される。
[処理1の結果, 処理2の結果, 処理3の結果]
- 結果が配列に格納されて出てくる
- promise.all内の処理のどれかをpromise化しなかったりするとどうなるのか不明
まあそんな感じでしょう。
詳しいことは分からないけど、使えるくらいには理解できてきた気がする。気のせいかもしれない。
とりあえず、次はasync/awaitを使っていきたい。