そうです。呼び出し中は「お待ち下さい」のような表示にしてfetch完了でデータを表示する流れになります。
やはりそうなんですね。
Express.jsではパラダイムが違うので出来ません。
JavaScript(以下JS)を使いましょう。
何故出来ないのか?
代替手段としてどうすれば良いのか?
この2つの順番で見ていきましょう。
まず、何故Express.jsではパラダイムが違うとかいう話になるのか
理由としてはNode.js + Express.jsで実現しているのがWebサーバだからです。
Webサーバとは違うものは作れません。
Webサーバというものはこんな仕様を表現する実装を指します
- TCP80番ポートで待ち受ける (別に設定次第で3000でも4000でも良いけど、デフォルトは80)
- HTTPリクエストをひたすら待ち構える
- HTTPリクエストを受け取ったら、送信者にHTTPレスポンスを返す
- クライアントとサーバはHTTPリクエスト→レスポンスによる文字のやり取りを1回やったら終わり
HTTPリクエストやレスポンスはMDNのサイトを見てください。
このHTTPレスポンスには一枚ペラのHTMLファイルが起点です。
例えばChrome等のWebブラウザでWebサイトを訪問した場合、
まずHTTPリクエストによりHTMLファイルを受け取ります。
そのHTMLファイルを分析・展開しながら必要なCSS・JS・画像ファイル等を追加でHTTPリクエストを飛ばしながら集めて画面を作っていく想定になっています。
なので「呼び出し中にお待ち下さいのような表示」用のHTMLを
クライアント(Webブラウザ)に投げてしまうとそこでおしまいです。
というかWebやHTTP通信の仕様がそういうもんなので、そこで時系列・状態の変化を作る事は出来ません。
でも、そういう動きするWebサイト実際にあるじゃん!
それは別の技術であるJSを利用しています。
JSは過去にダイナミックHTMLと呼ばれていた技術です。
HTMLは特定ルールに従って記述された単なる文字列なので、
人間にとっても機械にとっても読み辛いファイルとなります。
なのでWebブラウザはHTMLを解析して、
DOMツリーという機械が利用しやすい形に加工してメモリ上に蓄え、
そのDOMツリーを基準に画面に描画します。
JSはWebブラウザ上でDOMツリーが展開された後に実行されます。
(厳密には展開を途中で中断させて強制実行しますが、バグの原因なので開発者は初手でDOMツリー展開後まで遅延させて実行させるようコントロールしています)
JSは「マウスがクリックされた時」、「ファイルがロードされた時」
何らかのタイミングを待って、任意のタイミングでDOMツリーを更新します。
このために用意されたのがJSというスクリプト言語です。
- Webサーバとして稼働して、HTMLファイル(等)を配信する為に作られたNode.js
- Webブラウザの1機能として稼働して、HTMLファイルを受け取った後に画面更新を受け持つJavaScript
Node.jsはサーバサイドJSだよなんて呼ばれていますが、
用途や動く場所が全く別物です。
なのでごっちゃにしてはいけません。
必ずHTMLという紙切れ作ってHTTPレスポンスに乗せて返すのが先。
完成品のHTMLを受け取って、そのHTMLを更に添削するという流れを意識しましょう。
ejsという技術はNode.js上で、
即席のHTMLファイルを作るための技術です。
データが足りなければ実行する前に動作を停止させて全部集めろよという話になりますから、
非同期処理やPromiseという概念はありません。
ejsの中でJavaScriptに動いて欲しければ、
ejsの中でscript
タグを準備して、
HTMLとして完成した後に、Webブラウザに読み取って実行してもらう事を期待するだけになります。
(Chrome等の多くのWebブラウザは実行してくれるでしょうけど、テキストブラウザみたいな一部環境はscriptタグを無視します)
fetchするまでindex.ejsで待たせたい
じゃあ遅延ロードの仕組み出来ないじゃん!
Ajaxを実現させましょう。
JSにはDOMツリーを修正する以外にも、
裏でHTTPリクエストを発射して、結果が帰ってきたら実行するという仕組みが用意されています。
前述の通り、HTMLを作るのが先、JSの実行は後なので、
既存のejsの流れを汲むのであればscript
タグを用意してそこで実行するんでしたね。
apiの情報は「お待ちください」の初期画面を作る上では不要なので、
別のパスで待つようにしてJSONを返す設計にしましょう。
こんな感じになるはずです。
js
1/* router.js */
2const express = require('express');
3const router = express.Router();
4const api = require('../api');
5const newapi = require('../newapi');//newapiはfetchを実行するファイル
6
7router.get('/', async (req, res) => {
8 res.render('index', {
9 json: await api() // newapiの情報はお待ちください画面には不要なので削った
10 });
11});
12
13// 新しいエンドポイントを増設したよ
14router.get('/newapi', async (req, res) => {
15 res.json(await newapi());
16})
ejs
1<body>
2 <h1 id="title">ようこそ</h1>
3 <div id="category">
4 <p id="cate"></p>
5 <p id="diff"></p>
6 </div>
7 <hr>
8 <p id="p">以下のボタンをクリック</p>
9 /* こんな感じでやりたい事を書いていく */
10
11 /* scriptタグをベタ書きするなら閉じbodyタグの直前が慣習 */
12 <script>
13 const main = async () => {
14 const res = await fetch('/newapi');
15 const json = await res.json();
16
17 // ブラウザのF12等でデベロッパーツールのコンソールを開いて結果が画面上に表示される事を確認
18 console.log(json);
19
20 // TODO:変数jsonの値に従って、JSの流儀でDOMを更新するコードを書く
21 }
22 main();
23 </script>
24</body>
話が脱線しましたが、
ゆくゆくはVueあたりを使ったり、Express.jsをVue環境にまるっと移植する
Nuxt.jsに移行したりするのが無難だろうなぁと思いますが、
とりあえず、JSとNode.jsをきっちり分離して考えなきゃダメだよって感じの結論で締めて終わります。