- 投稿日:2020-05-29T23:43:16+09:00
Node.jsとMongoDBのAPIをHerokuにデプロイするまで
はじめに。
私は今、Reactと、Nodeでwebアプリを作っています。EC2に環境立ち上げるのはクソだるい... ってことで今までRailsでしか使ってなかったHerokuを使うことに。
Reactは超簡単にデプロイできて奇跡だと思った。Nodeも考える時間含めて2時間くらいでデプロイできた。奇跡!!!
セールスフォースの株買って大切に大切に保持しておきたい。
Node.jsとMongoDBのAPIをHerokuにデプロイ成功した絵
単純にルートで文字返してるだけ。
一番最初はProcfileを作る
僕はローカルで、
% node app/app.jsでアプロを起動させていたので、
% pwd /Users/ryosuke-hujisawa/Desktop/アプリ // ここにProcfile作った % ls README.md app node_modules package-lock.json package.json$ touch Procfile //これで動いた web: node app/app.js次、herokuアプリを作ろう
$ heroku create herokuアプリの名前 //これでリモート確認できるよ $ git remote -vProcfileをHerokuにまずはPushします。
$ git add . $ git commit -m "setting up to push to Heroku" $ git push heroku masterプロダクション Databaseを作ります。
$ heroku addons:create mongolab:sandboxDBの設定を書き換えよう
僕は元々こうなってたのを
module.exports = { 'secret': 'oauthServerSampleSecret', 'database': 'mongodb://localhost/server_oauth' }こうした
module.exports = { 'secret': 'oauthServerSampleSecret', 'database': process.env.DEV_MONGODB_URI || process.env.MONGODB_URI }
process.env.DEV_MONGODB_URI
. これは.envに書く。で、
process.env.MONGODB_URI
はherokuで設定する。これですね↓次、dotenvをインストールしよう
run npm install dotenv もしくは yarn add dotenvで、dotenvを確実に、一番上に僕が読み込んだ。
require('dotenv').config() //dotenvの下にdbの設定書く var config = require('./config'); //ここにdbの設定 var express = require('express'); var app = express();で、Herokuにpushする
$ git push heroku master終わり!
おめでとうございます。
$ heroku openこれで、NodeJSのapiをHerokuで運用できるね。
参考
How to deploy your Node.js / MongoDB app to the web, using Heroku
余談
途中でエディタ権限で保存できなくなってこのコマンド使った。
sudo chmod -R 777 プロジェクトディレクトリ
- 投稿日:2020-05-29T22:51:25+09:00
VSCodeのSSH接続機能で、RaspberryPi内のNode.jsコードをデバッグ
はじめに
本記事は、Pythonでのデバッグ実行と同内容を、Node.jsで実行した記事です。
Node.jsでもブレークポイント付きの快適デバッグが実現できました!
必要なもの
・RaspberryPi (本例ではRaspberryPi3 ModelB)
※あらかじめNode.jsをインストールしてください(最新のRaspbian10ではプリインストール)
・上記と同じネットワークにつながったPC (本例ではWindows10)
・Visual Studio Code 1.39.2以降(上記PCにインストール、本例では1.44.2使用)手順
下記記事を参考にさせて頂きました
https://qiita.com/GRGSIBERIA/items/b8cd4a2b3635d1bb0391※手順④まではPythonのときと同内容です
①SSH接続の設定と、configファイルの保存
本記事「公開鍵認証の場合」を参考に、SSH公開鍵認証の確立とconfigファイルの保存を実施してください。
なお、configファイルはPC内の下記フォルダに保存してください
C:\Users\[ユーザー名]\.ssh②VSCodeのインストール
PC側にVSCodeをインストールしてください
参考記事
https://qiita.com/psychoroid/items/7d85ae6bade4a67aedb1③Remote Developmentのインストール
VSCodeを起動し、下記の手順でRemote Developmentをインストールしてください
※Remote - SSHなど必要な関連ツールも、Remote Developmentをインストールすると同時にインストールされます。④RaspberryPiに接続
下図の手順で、接続するSSHサーバ = RaspberryPiを選んでください(下図の場合「raspi」)
新たにウインドウが開き、プラットフォーム選択を求められるので、Linuxを選ぶ
公開鍵認証のパスワードを求められるので、入力してEnterを押す
初回接続は時間が掛かるので、しばらく待つ。
成功すると、下図の赤枠をクリックしてRaspberryPi内のファイルにアクセスできる
⑤RaspberryPi内にコードを作成
ここから先はコンソールでも良いですが、GUIで操作する前提で記載を進めます。
・空ファイルの作成
「File」→「New File」で空のファイルを作成します
・コード内容の記載
例えば、以下のようなコードを記載します(配列の合計を求めて表示)test.jsvar a = [1, 3, 5, 7, 9]; var b = a.reduce(function(x, y){return x + y;}); console.log(b);・コードの保存
「File」→「SaveAs」→保存パスを指定→「OK」
・コンソールから実行してみる
「Terminal」→「New Terminal」でコンソールを開き
コードのあるフォルダに移動し、node test.js正常に結果がコンソール出力されていることが分かります。
⑥作成したコードをデバッグ実行
Pythonのときと同様、VSCodeのGUIでコードのデバッグまで出来てしまいます。
ブレークポイントで停止もできるので、開発効率が上がること間違いなしです!・ESLintのインストール
ESLintとは、Javascriptのコード分析ツールです。
なくてもデバッグ自体はできるようですが、エラーチェックによる効率向上のため、インストールします。既にPCローカルでインストールしている場合も、「install in SSH:[ホスト名]」をクリックしてRaspberry Pi内にもESLintをインストールします。
インストールが終わると「Reload Required」と出てくるので、クリックして再起動
・デバッグの実行
デバッグしたいコードを開いたのち、下記の操作を実行
・ブレークポイントの動作確認
下図のようにブレークポイントで処理を止め、変数内容の確認も可能です。
以上で、VSCodeでRaspberryPi内のNode.jsコードを遠隔デバッグすることができました。
GUIでサクサク開発できる環境が整い、個人的には満足の結果です!
- 投稿日:2020-05-29T20:44:39+09:00
JupyterLabでnode.jsのカーネルが立たない
同じ状況に立つ人が果たして居るのか分からないので、雑にメモ
環境:
win10
anaconda 4.8.3起こった問題
node.jsのカーネルが立たない
>jupyter lab [I 19:33:43.066 LabApp] JupyterLab extension loaded from C:\ProgramData\Anaconda3\lib\site-packages\jupyterlab [I 19:33:43.066 LabApp] JupyterLab application directory is C:\ProgramData\Anaconda3\share\jupyter\lab [I 19:33:43.486 LabApp] Serving notebooks from local directory: C:\Users\fuga [I 19:33:43.487 LabApp] The Jupyter Notebook is running at: [I 19:33:43.487 LabApp] http://localhost:8888/ [I 19:33:43.487 LabApp] Use Control-C to stop this server and shut down all kernels (twice to skip confirmation). [I 19:33:45.892 LabApp] 301 GET /lab/workspaces/auto-Z/?clone (::1) 1.01ms [I 19:33:47.494 LabApp] Build is up to date [I 19:33:49.648 LabApp] Creating new notebook in / [E 19:33:50.113 LabApp] Failed to run command: ['ijskernel.cmd', '--hide-undefined', 'C:\\Users\\hoge\\AppData\\Roaming\\jupyter\\runtime\\kernel-6cf3f0a8-3fed-475d-b293-b37723c04594.json', '--protocol=5.0']当然コードの実行もできない
[E 19:33:52.012 LabApp] Uncaught exception POST /api/sessions?1590748431976 (::1) HTTPServerRequest(protocol='http', host='localhost:8888', method='POST', uri='/api/sessions?1590748431976', version='HTTP/1.1', remote_ip='::1') Traceback (most recent call last): File "C:\ProgramData\Anaconda3\lib\site-packages\tornado\web.py", line 1703, in _execute result = await result File "C:\ProgramData\Anaconda3\lib\site-packages\tornado\gen.py", line 742, in run yielded = self.gen.throw(*exc_info) # type: ignore File "C:\ProgramData\Anaconda3\lib\site-packages\notebook\services\sessions\handlers.py", line 72, in post type=mtype)) File "C:\ProgramData\Anaconda3\lib\site-packages\tornado\gen.py", line 735, in run value = future.result() File "C:\ProgramData\Anaconda3\lib\site-packages\tornado\gen.py", line 742, in run yielded = self.gen.throw(*exc_info) # type: ignore File "C:\ProgramData\Anaconda3\lib\site-packages\notebook\services\sessions\sessionmanager.py", line 88, in create_session kernel_id = yield self.start_kernel_for_session(session_id, path, name, type, kernel_name) File "C:\ProgramData\Anaconda3\lib\site-packages\tornado\gen.py", line 735, in run value = future.result() File "C:\ProgramData\Anaconda3\lib\site-packages\tornado\gen.py", line 742, in run yielded = self.gen.throw(*exc_info) # type: ignore File "C:\ProgramData\Anaconda3\lib\site-packages\notebook\services\sessions\sessionmanager.py", line 101, in start_kernel_for_session self.kernel_manager.start_kernel(path=kernel_path, kernel_name=kernel_name) File "C:\ProgramData\Anaconda3\lib\site-packages\tornado\gen.py", line 735, in run value = future.result() File "C:\ProgramData\Anaconda3\lib\site-packages\tornado\gen.py", line 209, in wrapper yielded = next(result) File "C:\ProgramData\Anaconda3\lib\site-packages\notebook\services\kernels\kernelmanager.py", line 168, in start_kernel super(MappingKernelManager, self).start_kernel(**kwargs) File "C:\ProgramData\Anaconda3\lib\site-packages\jupyter_client\multikernelmanager.py", line 158, in start_kernel km.start_kernel(**kwargs) File "C:\ProgramData\Anaconda3\lib\site-packages\jupyter_client\manager.py", line 305, in start_kernel self.kernel = self._launch_kernel(kernel_cmd, **kw) File "C:\ProgramData\Anaconda3\lib\site-packages\jupyter_client\manager.py", line 212, in _launch_kernel return launch_kernel(kernel_cmd, **kw) File "C:\ProgramData\Anaconda3\lib\site-packages\jupyter_client\launcher.py", line 135, in launch_kernel proc = Popen(cmd, **kwargs) File "C:\ProgramData\Anaconda3\lib\subprocess.py", line 800, in __init__ restore_signals, start_new_session) File "C:\ProgramData\Anaconda3\lib\subprocess.py", line 1207, in _execute_child startupinfo) FileNotFoundError: [WinError 2] 指定されたファイルが見つかりません。治るまでに試したこと事
1. 一応nodeが入っているか確認
>conda install nodejs >conda update -all2. karnel.jsonを作り直してみる
>npm install -g ijavascript >ijsinstall >jupyter kernelspec list Available kernels: javascript C:\Users\hoge\AppData\Roaming\jupyter\kernels\javascript python3 C:\ProgramData\Anaconda3\share\jupyter\kernels\python3大丈夫そう
3. ここでjupyter labをまた起動してみると、エラーが見えるようになった
エラーの内容↓
Error: The module '\\?\C:\Program Files (x86)\Nodist\bin\node_modules\ijavascript\node_modules\zeromq\build\Release\zmq.node' was compiled against a different Node.js version using NODE_MODULE_VERSION 64. This version of Node.js requires NODE_MODULE_VERSION 67. Please try re-compiling or re-installing the module (for instance, using `npm rebuild` or `npm install`). at Object.Module._extensions..node (internal/modules/cjs/loader.js:846:18) at Module.load (internal/modules/cjs/loader.js:672:32) at tryModuleLoad (internal/modules/cjs/loader.js:612:12) at Function.Module._load (internal/modules/cjs/loader.js:604:3) at Module.require (internal/modules/cjs/loader.js:711:19) at require (internal/modules/cjs/helpers.js:14:16) at Object.<anonymous> (C:\Program Files (x86)\Nodist\bin\node_modules\ijavascript\node_modules\zeromq\lib\index.js:6:11) at Module._compile (internal/modules/cjs/loader.js:805:30) at Object.Module._extensions..js (internal/modules/cjs/loader.js:816:10) at Module.load (internal/modules/cjs/loader.js:672:32) [I 19:38:54.040 LabApp] KernelRestarter: restarting kernel (4/5), new random ports internal/modules/cjs/loader.js:846 return process.dlopen(module, path.toNamespacedPath(filename));明らかにnodejsのバージョンが違うよと言われている。
そういえばアップデートした気がする。NODE_MODULE_VERSION 64. This version of Node.js requires NODE_MODULE_VERSION 67. Please try re-compiling or re-installingNODE_MODULE_VERSION が 67だけど、64にしてほしいとのこと
4. nodejsのバージョンを変える
リリース一覧 | Node.js
ここを見に行って、NODE_MODULE_VERSIONが64のものを選ぶ。
私はなんとなく10.12.0にすることにした。
バージョン変更にはもう入れてあったnodistを使った。>nodist 10.12.0 10.12.0 Installing 10.12.0 10.12.0 [===============] 24201/24201 KiB 100% 0.0s Installation successful. >nodist (x64) 8.16.2 > 10.12.0 (global: 10.12.0) 11.13.05. やれと言われていたリビルドをする。
>jupyter lab build [LabBuildApp] JupyterLab 1.2.6 [LabBuildApp] Building in C:\ProgramData\Anaconda3\share\jupyter\lab [LabBuildApp] Building jupyterlab assets (build:prod:minimize) -成功したとも失敗したとも出ないが、多分成功している。
6. jupyter labを起動してみる
>jupyter lab
- 投稿日:2020-05-29T18:49:44+09:00
SlackBOT Node.js axios await asyncを使ったPOSTリクエスト
はじめに
最近のJavaScriptでGET,POSTリクエストするには
axios
を使うのがイケてるらしいが、非同期の処理になるためコールバック地獄が起こる。それを解決するためにasync
await
を使ったPOSTリクエストのサンプルコード(個人的なメモ)SlackBOTに指定のチャンネルにテキストを投稿させるためのコードです。
カスタムインテグレーションではなく、「App」の方です
基本的なPOSTリクエストなので応用は効くと思いますコード
node.jsconst axios = require('axios');//npm install axios してね //Slackにメッセージを送る //引数1(文字列) : チャンネル名 (例: #勤怠履歴) //引数2(文字列) : 送りたいメッセージ const postSlack = async (ch,msg) =>{ console.log('postSlack...') const req_url = 'https://slack.com/api/chat.postMessage' console.log('req_url:' + req_url); //これを使わずにオブジェクトで送るとJSONの形式ガーーーー!!みたいなErrorがでます let params = new URLSearchParams(); params.append('token','アクセストークン') //正式なものをいれてください params.append('channel',ch) params.append('text',msg) const res = await axios.post(req_url, params) return res }呼び出し方
index.jsconst test = async () => { const result = await postSlack('#general','こんにちはせかい') console.log('result: ' + JSON.stringify(result.data)) }結果
うまく動かない場合は
SlackAppの管理画面から「OAuth & Permissions」→から以下の権限を与えてください(不要な権限があるとは思いますが、個人的な設定です
channels:manage
channels:read
chat:write
chat:write.customize
chat:write.public
遭遇したエラー
new URLSearchParams()
を使わないときに遭遇したエラー(node:73150) UnhandledPromiseRejectionWarning: TypeError: Converting circular structure to JSON --> starting at object with constructor 'ClientRequest' | property 'socket' -> object with constructor 'TLSSocket' --- property '_httpMessage' closes the circle at JSON.stringify (<anonymous>) at test (/Users/merarli/WebstormProjects/hogehoge/index.js:1143:32) at processTicksAndRejections (internal/process/task_queues.js:85:5) (node:73150) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1) (node:73150) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
- 投稿日:2020-05-29T18:13:14+09:00
AWS LambdaからSlackのIncoming Webhooksでメッセージを送る際のソースコードと複数メッセージを送る際の注意点
実装する機会があり、少々躓いた箇所があったので備忘録として残しておきます。
Lambda関数やIncoming Webhooksは準備できている想定として、これらの作成手順は省略します。環境
- macOS
- AWS Cloud9
- Node.js 12
実行コード
Lambda関数を1度実行する度に、Slackメッセージを1件だけ送るような場合は、以下のコードで動くと思います。
1度のLambdaで複数のメッセージを送る場合は、注意点があるので後ほど説明します。const env = process.env const request = require('request'); exports.handler = function(event, context) { // リクエスト設定 const options = { url: env.WEB_HOOK_URL, headers: { 'Content-type': 'application/json' }, body: { "username": "jinto", "text": "Hello !!" }, json: true }; //メッセージ送信 request.post(options); return "success !!"; }1度のLambdaで大量のメッセージを送る場合
2〜3件程度のメッセージであれば上記のコードをそのままfor文等で繰り返しても問題ないと思いますが、数十件〜数百件のメッセージを1度のLambdaで処理したい場合は注意が必要です。
仮に上記のコードを、for(let i = 0; i < 100; i++) { //メッセージ送信 request.post(options); }このように100回繰り返した場合、Slackにはメッセージが数件〜数十件しか届かないと思います。自分もここで躓きました。
どうやら、requestメソッドは非同期的に処理されるので、requestを100回実行し終わる前に、Lambda関数の実行そのものが終了してしまうようです。
大量のメッセージを送信したい場合には、requestをpromise化し、async〜awaitで1件ずつ止めてあげると上手く動きます。
Node.jsには、request-promise
という便利なモジュールがあったので、こちらを使用しました。コードを以下に示します。const env = process.env const requestPromise = require('request-promise'); exports.handler = async(event, context) => { // リクエスト設定 const options = { url: env.WEB_HOOK_URL, headers: { 'Content-type': 'application/json' }, body: { "username": "jinto", "text": "Hello !!" }, json: true }; for(let i = 0; i < 100; i++) { await requestPromise.post(options); // 通常のrequestだと非同期的に処理されるので、処理が終わる前にLambda関数が閉じてしまう } return "success !!"; }ご参考までに。
- 投稿日:2020-05-29T18:13:14+09:00
LambdaからSlackにメッセージを送る際のソースコードと複数回メッセージを送る際の注意点
AWSのLambda関数から、SlackのIncoming Webhooksでメッセージを送信する機会があり、少々躓いた箇所があったので備忘録として残しておきます。
Lambda関数やIncoming Webhooksは準備できている想定として、これらの作成手順は省略します。環境
- macOS
- AWS Cloud9
- Node.js 12
実行コード
Lambda関数を1度実行する度に、Slackメッセージを1件だけ送るような場合は、以下のコードで動くと思います。
1度のLambdaで複数回メッセージを送る場合は、注意点があるので後ほど説明します。const env = process.env const request = require('request'); exports.handler = function(event, context) { // リクエスト設定 const options = { url: env.WEB_HOOK_URL, headers: { 'Content-type': 'application/json' }, body: { "username": "jinto", "text": "Hello !!" }, json: true }; //メッセージ送信 request.post(options); return "success !!"; }1度のLambdaで大量のメッセージを送る場合
2〜3件程度のメッセージであれば上記のコードをそのままfor文等で繰り返しても問題ないと思いますが、数十件〜数百件のメッセージを1度のLambdaで処理したい場合は注意が必要です。
仮に上記のコードを、for(let i = 0; i < 100; i++) { //メッセージ送信 request.post(options); }このように100回繰り返した場合、Slackにはメッセージが数件〜数十件しか届かないと思います。自分もここで躓きました。
どうやら、requestメソッドは非同期的に処理されるので、requestを100回実行し終わる前に、Lambda関数の実行そのものが終了してしまうようです。
大量のメッセージを送信したい場合には、requestをpromise化し、async〜awaitで1件ずつ止めてあげると上手く動きます。
Node.jsには、request-promise
という便利なモジュールがあったので、こちらを使用しました。コードを以下に示します。const env = process.env const requestPromise = require('request-promise'); exports.handler = async(event, context) => { // リクエスト設定 const options = { url: env.WEB_HOOK_URL, headers: { 'Content-type': 'application/json' }, body: { "username": "jinto", "text": "Hello !!" }, json: true }; for(let i = 0; i < 100; i++) { await requestPromise.post(options); // 通常のrequestだと非同期的に処理されるので、処理が終わる前にLambda関数が閉じてしまう } return "success !!"; }ご参考までに。
- 投稿日:2020-05-29T15:52:54+09:00
node-addon-apiについて
Node-addon-api について
node.jsから、CやC++のAPIを直接実行するためには、
N-API を使用することになるが、非常に複雑で、コーディングが面倒になるので、
C++でN-APIをラップしたnode-addon-apiを使用したほうが良い。ただ、Javascriptの知識と、C++の知識(メモリ管理等)が必要となりますが、
基本的に細かい制御はクラスライブラリがやってくれるので、
実装に集中できます。ビルド方法
package.jsonの準備
まずは、package.json を準備する。
bindingsとnode-addon-apiをインストールします。npm init # 初期設定 npm install bindings node-addon-apibinding.gypファイル
binding.gypファイルを作成して、C++のビルト環境を作成します。
{ "targets": [ { "target_name": "hello", "cflags!": [ "-fno-exceptions" ], "cflags_cc!": [ "-fno-exceptions" ], "sources": [ "hello.cpp" ], "include_dirs": [ "<!@(node -p \"require('node-addon-api').include\")" ], 'defines': [ 'NAPI_DISABLE_CPP_EXCEPTIONS' ], } ] }hellow.cpp
cpp ファイルを作成します。
#include <napi.h> Napi::String Method(const Napi::CallbackInfo& info) { Napi::Env env = info.Env(); return Napi::String::New(env, "world"); } Napi::Object Init(Napi::Env env, Napi::Object exports) { exports.Set(Napi::String::New(env, "hello"), Napi::Function::New(env, Method)); return exports; } NODE_API_MODULE(hello, Init)C++の説明
NODE_API_MODULE(hello, Init)
javascriptとの接続するためのマクロ
Init 関数内でエクスポートするファンクションを定義する。
exports.SetでhelloというjavascriptのメソッドをMethodというC++関数に割り当てている。Method関数内で、worldという文字列を返している。
hello.js
var addon = require('bindings')('hello'); console.log(addon.hello()); // 'world'bindingsモジュールをロードして,
helloを呼び出します。ビルド
npm installを実行すると、C++のコンパイラが実行され、ビルドされます。
実行
node hello.jsを実行するとworldという文字列が表示されると思います。
終わりに
以下の例は、ほぼ node-addon-apiのサンプルを元にしました。
https://github.com/fore-vision/node-addon-api
上にサンプルとドキュメントがあるので、色々できると思います。
- 投稿日:2020-05-29T15:11:24+09:00
【JavaScript】forループ内でawaitする方法
経緯
forループ内で同期処理を行いたかったので調べてみたら for await...of というものがあることを知りました。
結論
このように
async
のなかにfor await...of
を書くことで forループ内でawait
を宣言できます。index.js// 対象の反復オブジェクト const targetArr = [1, 2, 3]; // 実行する関数 const sampleFunc = (value) => { // asyncの効果は各functionブロックで切れるので逐一指定が必要 return new Promise(resolve => { // 2秒待ってから計算結果をresolveする setTimeout(() => { console.log('Calculating...'); resolve(value * 2); }, 2000); }) } // for await...of文は必ずasyncの中で (async () => { for await (num of targetArr) { // 関数の実行結果を格納して表示 const result = await sampleFunc(num); console.log(result); } })();for await...of とは
for await...of 文は非同期(と同期)の反復オブジェクトを繰り返して処理するループを作ります。対象の反復オブジェクトは、ビルトインの String、Array、配列様オブジェクト( arguments、NodeList 等)、TypedArray、Map、Set、さらに、ユーザーが定義した非同期・同期の反復オブジェクトが含まれます。オブジェクトの各プロパティの値に対して実行されるステートメントを使用してカスタム反復フックを呼び出します。
簡単に言うと
反復オブジェクト(ArrayやObjectなど)の中で同期処理を行う事ができる文です。ESLintでは非推奨
便利な構文ですが、ESLintでは設計思想的な意味で推奨されていません。
https://eslint.org/docs/rules/no-await-in-loop
Performing an operation on each element of an iterable is a common task. However, performing an await as part of each operation is an indication that the program is not taking full advantage of the parallelization benefits of async/await.
Usually, the code should be refactored to create all the promises at once, then get access to the results using Promise.all(). Otherwise, each successive operation will not start until the previous one has completed.
Concretely, the following function should be refactored as shown:反復の各要素に対して操作を行うことは一般的な作業です。
しかしながら、各段階の操作でawait
を実行するとasync/await
による並列化の利点を十分に活用できません。
一般にこのようなコードは、全てのプロミスを一度に作成しPromise.all()
を用いて結果を得るようにするべきです。