結論から言えば
Promiseとasync / await構文を勉強してください。
現在使っているrequestモジュールはPromise非対応です
仮にrequest-promise-nativeにしようと決定したとして、こんな感じになります。
bash
1# request-promise-nativeをインストール
2$ npm install request-promise-native
js
1const request = require('request-promise-native')
2const list_url= 'https://hogehoge/'
3
4// async関数を定義することで内部でawaitを使用可能となる
5async function main(){
6 for(let page_num = 1; page_num < 5; page_num++){
7 try {
8 body = await request(list_url)
9 http_get(body)
10 // list_urlはconst定義されてるから動かないんじゃね?
11 list_url = list_url + String(page_num)
12 } catch (e) {
13 console.error(e)
14 }
15 }
16}
17main()
なんでこうなるのかを順を追って解説します。
まずJavaScriptはシングルスレッドなので待つという事はしません。
sleep関数はありませんし、HTTPリクエストを送信したら帰って来るまで待ったりしません。
待ってるとブラウザが固まってフリーズしますから。
Node.jsもシングルスレッドなので同様、フリーズするので待てない。
いやいや、JSってボタンがクリックされたらHTML書き換える言語やないか、待ってるだろ!
待つためには仕組みが必要です。
「待つ」を実現するために、JSにはイベントという仕組みが存在します。
JSにはイベント置き場があり、達成条件と関数(達成後実行して欲しい処理)をセットで登録出来るようになっています。
質問文のrequest(list_url, fn)
がそうですね。
これはイベント登録申請が受理されただけで、その行の動作自体は一瞬で完了します。
もしfor文で10件回すと、瞬時に10件のイベント登録申請が完了されてしまいます。
JSはすべての行の実行を行い動作を終了させると、
イベントループという待機状態に入ります。
イベントループでは達成条件を満たしたか否かを巡回して、
達成したイベントが見つかった場合、対になっている関数を一つ取り出して実行します。
そしてまた巡回に戻る。
なので質問文を愚直にやるならばfor文は使えません。
関数の中に関数を入れて、その中にまた関数を入れるという入れ子構造を作っておき、
それをrequestのコールバック関数として指定する形になります。
js
1request(list_url, (e, response, body) => {
2 http_get(body)
3 page_num = 0
4 list_url = list_url + String(page_num)
5 request(list_url, (e, response, body) => {
6 http_get(body)
7 page_num = 1
8 list_url = list_url + String(page_num)
9 request(list_url, (e, response, body) => {
10 http_get(body)
11 page_num = 2
12 list_url = list_url + String(page_num)
13 request(list_url, (e, response, body) => {
14 http_get(body)
15 })
16 })
17 })
18})
4回リクエストを飛ばす仕組みはこんな感じ
これをコールバック地獄と呼びます。
そこで、JSの新しい仕様のES2016でPromiseが導入されました。
使い方に関しては勉強してほしいのですが、
それによりコードが多少簡素になりました。
js
1// Promise対応モジュール
2const request = require('request-promise-native')
3
4request(list_url)
5 .then(body => {
6 http_get(body)
7 page_num = 0
8 list_url = list_url + String(page_num)
9 return request(list_url)
10 })
11 .then(body => {
12 http_get(body)
13 page_num = 1
14 list_url = list_url + String(page_num)
15 return request(list_url)
16 })
17 .then(body => {
18 http_get(body)
19 page_num = 2
20 list_url = list_url + String(page_num)
21 return request(list_url)
22 })
23 .then(body => {
24 http_get(body)
25 })
さっきよりはまだ……うん、って感じにはなりましたね。
この.then
がイベント登録なのでPromiseインスタンスに対して
forで.then
の登録を一気にやれば実現の可能性も見えてきます。
ですが、所詮イベント登録なので直感的にfor文を使う事は出来ません。
そこでようやくasync/await構文です。
awaitはPromiseインスタンスの実行完了を待ち、
自動的に.thenを叩いて中の値を抽出してくれる糖衣構文です。
回答冒頭のような事が可能となり、直感的なコードを書けるようになります。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2020/08/18 23:43