🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
Node.js

Node.jsとはGoogleのV8 JavaScriptエンジンを使用しているサーバーサイドのイベント駆動型プログラムです。

Express

ExpressはNode.jsのWebアプリケーションフレームワークです。 マルチページを構築するための機能セットおよびハイブリッドのWebアプリケーションを提供します。

Q&A

解決済

1回答

2160閲覧

node.js Express ejsファイル内でasync/await関数で値を取りたい。

hokosugi

総合スコア63

Node.js

Node.jsとはGoogleのV8 JavaScriptエンジンを使用しているサーバーサイドのイベント駆動型プログラムです。

Express

ExpressはNode.jsのWebアプリケーションフレームワークです。 マルチページを構築するための機能セットおよびハイブリッドのWebアプリケーションを提供します。

0グッド

0クリップ

投稿2021/02/26 05:11

ejsファイルで他ファイルで定義し,exportsされた関数をasync/awaitで実行したいがPromiseが返ってきます。
非同期関数はejsで利用できないのかどうかは不明ですが、他の方法も含め値を取るにはどうしたらよいでしょうか?

/* index.ejs */ <body> <%# ここにasync-awaitのapi関数を定義して非同期処理中に「ようこそ」以下を表示させたい %> <% async function apiWait(){ return await api; } %> <h1 id="title">ようこそ</h1> <div id="category"> <p id="cate"></p> <p id="diff"></p> </div> <hr> <p id="p">以下のボタンをクリック</p> <hr> <button id="btn" name="btn">開始</button> <div id="answers"></div> <%# api関数実行 %> <%# apiWait(); %> <% const json2 = apiWait(); %> <% console.log('json2::::'+json2); %> <% console.log('json::::'+json); %> <%# 上の出力:'json2::::[object Promise]' え、json2でobject返ってこない???(jsonはobjectが返ってきている)%> <% if (json) { %> /* 以下、略 */
/* router.js */ const express = require('express'); const router = express.Router(); const api = require('../newapi');//newapiはfetchを実行するファイル router.get('/', async (req, res, next)=> { const json = await api();//値を待ってdataを取得 res.render('index', { api: api(),//async-await関数を作成しfetchするまでindex.ejsで待たせたい json: json, }); });

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

miyabi-sun

2021/02/26 05:17

ん?もしかしてこれはブラウザで先に画面開いて 暫く経ってから画面上の表示を更新するといった事がやりたいのでしょうか?
hokosugi

2021/02/26 05:32

miyabi-sunさん、先日はご回答ありがとうございました、助かりました。そうです。呼び出し中は「お待ち下さい」のような表示にしてfetch完了でデータを表示する流れになります。
guest

回答1

0

ベストアンサー

そうです。呼び出し中は「お待ち下さい」のような表示にして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をきっちり分離して考えなきゃダメだよって感じの結論で締めて終わります。

投稿2021/02/26 07:59

miyabi-sun

総合スコア21203

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

hokosugi

2021/02/26 08:57

なんと、長文かつ丁寧なご回答、本当にありがとうございます。痛み入ります。家に帰り次第、身を正し、心して読みたいと存じます。取り急ぎ、お礼まで。
hokosugi

2021/02/26 22:08

三度読み直してようやく理解できました。そもそもnode.jsで安易に何でも出来ると考えるほうがいけないかったわけですね。エンドポイントを迂回する工夫を教えてもらって事なきを得ましたが、出来ないこともあると考えたほうが良いわけですね。「Express.jsをVue環境にまるっと移植する Nuxt.jsに移行」、node.jsだけ学習すればそれで良いと思っていましたが、フロントとバックサイドが一体化して初めて完成体になると分かっただけでも、理解不足の私にも収穫でした。ありがとうございました。ちなみに'res.json(await newapi());'で'TypeError: Converting circular structure to JSON'のエラーが出まして、'json-cyclic'モジュールを入れて解決しました。(それでお返事が今朝になりましたw)
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問