20211014のNode.jsに関する記事は8件です。

コロナ禍前後で、クラウドファンディングへの関心は変わったのか?

はじめに はじめまして、JavaScript初心者です。 恥ずかしながらAPIについても数日前に初めて意味を知った素人。当然ながらQiitaAPIに触れたのも初めて。知識を武器にできるよう、楽しみながら学んで行きたいです。 目的 どうせやるなら自分が好奇心を持てるものを。 思い浮かんだのは、最近よく耳にするクラウドファンディング(以下、CF)について。 数年前に小学校の同級生が、子供の頃からの夢を実現させるためCFを達成させ起業した話を聞き、とても夢があると感動した。 2021年10月現在はコロナ禍真っ只中。私がCFで初めて支援したのも昨年コロナが蔓延し始めた頃だった。 ちなみに当時支援したのは、こちら。 プリントごっこを思い出す、昔懐かしい印刷が楽しめる印刷所。シルクスクリーン発注をして自分で手刷り印刷もできるので、いつか利用してみたいなぁと思っていたところ、経営難となり経営存続を賭けてCFが発起された。(結果は是非、リンク先からご確認を!) 同じようにコロナの影響でCFに救いの手を伸ばした企業、個人も多いはず。 Qiita内でも、コロナ禍前後(2020年3月)でCFに関する変化があったのか、調べてみる。 使用した環境 Visual Studio Code 1.61.0 Node.js 7.24.0 確認方法 やりたいことは、以下の3つ。 本文に「クラウドファンディング」を使用している記事を検索 検索した記事の作成日を集計 コロナ前後(2020年3月前後)で記事数に変化があるのか確認 本文に「クラウドファンディング」を含む記事を検索し、作成日を出力する まずは単純に、Qiita内で本文に「クラウドファンディング」という単語を使用している記事の情報を抽出する。 出力にcreated_atを指定して、記事の作成日付を出力する。 // axiosモジュールを読み込む const axios = require("axios"); // main()関数を定義する async function main() { // QiitaAPIで本文に「クラウドファンディング」を含む記事を検索 let response = await axios.get( "https://qiita.com/api/v2/items?per_page=100&query="+encodeURIComponent("クラウドファンディング") );   // 結果を出力(繰り返し) for (let i = 0; i < response.data.length; i++) { console.log(response.data[i].created_at); } } main(); 結果(失敗) pageのパラメータ指定なしでper_page=100と記載したため、現時点から過去100件分しかデータが取得できなかった。 これだと、2018年12月からのデータしか取れていない。そのため8件と極端に少なくなっている。データとして非常に気持ち悪い結果になった。せめて2018年について、1年分データを取りたい。 件数を制限することなく、2018年1月から情報を取得するにはどうしたらよいか? 本文に「クラウドファンディング」を含む記事の総件数を確認する 取得件数の上限が100件だと、件数が取得しきれないということが分かった。 そもそも「クラウドファンディング」で検索した場合の関連記事は全部で何件あるのか? 出力指定に、console.log(response.headers); を指定。 返ってきたデータからtotal-countを出力し、検索対象の総件数を確認する。 Total-Countについて また、ページを指定できるAPIでは、要素の合計数が Total-Count レスポンスヘッダに含まれます。 // axiosモジュールを読み込む const axios = require('axios'); // main()関数を定義する async function main() { // QiitaAPIで「クラウドファンディング」という単語で記事を検索 let response = await axios.get('https://qiita.com/api/v2/items?query=' + encodeURIComponent('クラウドファンディング') ); //取得した記事の情報を抽出 console.log(response.headers); } main(); 以下が、実行結果のtotal-countの件数。 2021年10月現在、185件の記事があることを確認できたので、次は先ほど取得し損ねた2018年分が丸っと入ったデータを取得する。 指定した年月以降のデータを取得する 今回取得したいデータはMAXで185件ということを確認したので、出力上限を2ページ(200件分)に設定する。 2018年1月分から取得したいので、encodeURIComponent('>=2018-01')を指定する。 // axiosモジュールを読み込む const axios = require("axios"); // main()関数を定義する async function main(page) { for(let page=1; page<=2; page++) { //「クラウドファンディング」という単語を含み、2018年1月~現在の期間に作成された記事を検索 let response = await axios.get( 'https://qiita.com/api/v2/items?page=' + page + '&per_page=100&query=' + encodeURIComponent('クラウドファンディング') + '+created:' + encodeURIComponent('>=2018-01') ); // 結果を出力(繰り返し) for (let i = 0; i < response.data.length; i++) { console.log(response.data[i].created_at); } } } main(); 取得の上限を200件にしたが、もっと件数を取得したい場合は、page<=2 の数字を変更する。 結果 ・2018年~2021年10月現在の集計結果 取得したデータの最終行を確認すると、2018-01-01のデータが含まれているので、データを上手く取得することができた。(以下、データ一部抜粋) グラフ化してみても、2020年で件数が確かに増えている。 しかし正直もっと分かりやすく差が出るかと思っていたが、それほど差がないようにも見える。(2018年の件数が予想よりも多かった。) ・2020年の集計結果 2020年のデータを月単位で比較すると、分かりやすく3月(コロナ後)で数が増えていることが確認できた。 すべてのデータを取得する せっかくなので、2018年以前も含めたすべてのデータも取得する。 先のコードの年月指定の行を削除して実行。 // axiosモジュールを読み込む const axios = require("axios"); // main()関数を定義する async function main(page) { for(let page=1; page<=2; page++) { //「クラウドファンディング」という単語を含む記事を検索 let response = await axios.get( 'https://qiita.com/api/v2/items?page=' + page + '&per_page=100&query=' + encodeURIComponent('クラウドファンディング') ); // 結果を出力(繰り返し) for (let i = 0; i < response.data.length; i++) { console.log(response.data[i].created_at); } } } main(); 結果をグラフ化すると、CFへの関心度が如実に表れているように見える。 CAMPFIREによれば、日本で初めてのCFは2011年9月とのこと。(参考:クラウドファンディングのはじまり) Qiitaで「クラウドファンディング」文言を含む記事が初投稿された2013年は、CFサービスMakuakeが提供開始した年でもあり、世間的にCFに関する認知度が高まったと推測される。 月別の比較結果 ちなみに2013年から2021年10月現在時点で「クラウドファンディング」文言が入った記事の投稿数が一番多かった月は、ダントツで12月だった。 実際の記事を確認すると、文言がタイトルに入ったリンクを貼った記事が複数投稿されているのでその影響もあるようだが、それにしても突出して件数が多いので興味深い。 12月になるとCF関連で何かあるのだろうか? 結論 コロナ後に、CFへの関心は高まったと言える。 12月になると、Qiitaに「クラウドファンディング」の文言が含まれた記事が多く投稿される。 最後に データの集計や分析をするのに、QiitaAPIを活用して効率よくデータが取得することができた。 今回はデータの加工については手作業で実施してしまったが、csv形式での出力、グラフ化、日付のFROM TOで指定、等を自動で取得できると理想的である。 データを数字としてだけでなく、グラフや表で可視化すると新たな発見があったりして、とても興味深かった。 数字に変化があったとき、世間もまた変化していて、それがどんなものなのか調べたり考えたりすることで思わぬ気づきがあると知ることができた。比較数が少ないとそれほど差がないように見えるが、比較対象を増やすことで緩やかな変化に気づくこともあるというのも、大きな発見だった。 2021年データについてまだあと2ヶ月分のデータが空白なので、2022年になったら改めて取得して比較したい。 結果が今から楽しみである。 参考記事 QiitaAPIで2020年投稿のストック数が多い記事のタグ情報を調べてみた Qiita APIで投稿一覧を取得するときに、検索クエリをORでつなぐ時の注意点
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Electron】MenuItemConstructorOptionsの型推論

初書:2021/10/14 mac : 11.6 electron : 15.1.2 typescript : 4.4.3 前置き electronとtypescriptを使ってアプリケーションを開発中のこと。 メニューバーを作成しようと試みたときに起きた出来事。 「型推論が通らない」 ので、それの修正メモ。 該当コード 該当ページMenu | Electron Electronの日本語公式ページにある「サンプル」コードで問題は起きた。 ちなみにそのコードを、Typescript用に若干書き換えたものがこちら。 import { app, Menu } from ('electron'); const isMac = process.platform === 'darwin'; const template = [ // { role: 'appMenu' } ...(isMac ? [{ label: app.name, submenu: [ { role: 'about' }, { type: 'separator' }, { role: 'services' }, { type: 'separator' }, { role: 'hide' }, { role: 'hideOthers' }, { role: 'unhide' }, { type: 'separator' }, { role: 'quit' } ] }] : []), // { role: 'fileMenu' } { label: 'ファイル', submenu: [ isMac ? { role: 'close' } : { role: 'quit' } ] }, // { role: 'editMenu' } { label: 'Edit', submenu: [ { role: 'undo' }, { role: 'redo' }, { type: 'separator' }, { role: 'cut' }, { role: 'copy' }, { role: 'paste' }, ...(isMac ? [ { role: 'pasteAndMatchStyle' }, { role: 'delete' }, { role: 'selectAll' }, { type: 'separator' }, { label: 'Speech', submenu: [ { role: 'startSpeaking' }, { role: 'stopSpeaking' } ] } ] : [ { role: 'delete' }, { type: 'separator' }, { role: 'selectAll' } ]) ] }, // { role: 'viewMenu' } { label: 'View', submenu: [ { role: 'reload' }, { role: 'forceReload' }, { role: 'toggleDevTools' }, { type: 'separator' }, { role: 'resetZoom' }, { role: 'zoomIn' }, { role: 'zoomOut' }, { type: 'separator' }, { role: 'togglefullscreen' } ] }, // { role: 'windowMenu' } { label: 'Window', submenu: [ { role: 'minimize' }, { role: 'zoom' }, ...(isMac ? [ { type: 'separator' }, { role: 'front' }, { type: 'separator' }, { role: 'window' } ] : [ { role: 'close' } ]) ] }, { role: 'help', submenu: [ { label: 'Learn More', click: async () => { const { shell } = require('electron') await shell.openExternal('https://electronjs.org') } } ] } ]; const menu = Menu.buildFromTemplate(template); Menu.setApplicationMenu(menu); requireがimportになって個人的に気に入らなかったセミコロンがついただけ。 ここで下から2行目のMenu.buildFromTemplate(template);のtemplateにエラーが付きます。 エラーの原因 まずはエラー文はこちら。 型 '({ label: string; submenu: ({ role: string; type?: undefined; label?: undefined; submenu?: undefined; } | { type: string; role?: undefined; label?: undefined; submenu?: undefined; } | { label: string; submenu: { ...; }[]; role?: undefined; type?: undefined; })[]; role?: undefined; } | { ...; })[]' の引数を型 '(MenuItemConstructorOptions | MenuItem)[]' のパラメーターに割り当てることはできません。 型 '{ label: string; submenu: ({ role: string; type?: undefined; label?: undefined; submenu?: undefined; } | { type: string; role?: undefined; label?: undefined; submenu?: undefined; } | { label: string; submenu: { ...; }[]; role?: undefined; type?: undefined; })[]; role?: undefined; } | { ...; }' を型 'MenuItemConstructorOptions | MenuItem' に割り当てることはできません。 型 '{ label: string; submenu: ({ role: string; type?: undefined; label?: undefined; submenu?: undefined; } | { type: string; role?: undefined; label?: undefined; submenu?: undefined; } | { label: string; submenu: { ...; }[]; role?: undefined; type?: undefined; })[]; role?: undefined; }' を型 'MenuItemConstructorOptions | MenuItem' に割り当てることはできません。 型 '{ label: string; submenu: ({ role: string; type?: undefined; label?: undefined; submenu?: undefined; } | { type: string; role?: undefined; label?: undefined; submenu?: undefined; } | { label: string; submenu: { ...; }[]; role?: undefined; type?: undefined; })[]; role?: undefined; }' には 型 'MenuItem' からの次のプロパティがありません: checked, click, commandId, enabled、9 など。ts(2345) 読む気失せるほど長い。 単純にいうと、typeやroleがstring型になっているが、MenuItemConstructorOptions内ではストリングリテラル型(限定されたstring型)だよーというエラー。 これは、templateを定義する際に型指定をしていないため、単にstring型として認識されているのが原因。 ということで、templateに型をつける。 const template : Electron.MenuItemConstructorOptions[] = [ // 略 はい、終わりです。エラーの量が。 ...(isMac ? [] : []) // 上記コードの一部を切り取っているだけなので、当たり前ですが空の配列ではないです。 この辺が全部エラー吐きます。 解決策を考える おそらく三項演算子を使っているため、その箇所で型推論していないのが原因なのではないかと予想し、 // 上記と同じエラー ...(isMac ? [] as Electron.MenuItemConstructorOptions[]: []) // 型 'MenuItemConstructorOptions' には、反復子を返す '[Symbol.iterator]()' メソッドが必要です。ts(2488) ...(isMac ? [] : []) as Electron.MenuItemConstructorOptions[] いずれもダメ。 仕方ないので外部に切り出す。 let NameSubMenu : Electron.MenuItemConstructorOptions[] = []; if(isMac){ NameSubMenu = [ { label: app.name, submenu: [ { role: 'about' }, { type: 'separator' }, { role: 'services' }, { type: 'separator' }, { role: 'hide' }, { role: 'hideOthers' }, { role: 'unhide' }, { type: 'separator' }, { role: 'quit' } ] } ]; } const template : Electron.MenuItemConstructorOptions[] = [ // { role: 'appMenu' } ...NameSubMenu , // 以下略 これも結局エラーが出るので意味なし。 結論 誰かに頼る。 Create Electron Menu in TypeScript? - Stack Overflow pushにするといいとかなんとか。 ということで仕方なく全部pushに書き換えた。 const template: Electron.MenuItemConstructorOptions[] = []; const macMenu: Electron.MenuItemConstructorOptions[] = [ { label: app.name, submenu: [ { role: 'about' }, { type: 'separator' }, { role: 'services' }, { type: 'separator' }, { role: 'hide' }, { role: 'hideOthers' }, { role: 'quit' }, ], }, ]; if (isMac) { // { role: 'appMenu' } template.push(...macMenu); } // { role: 'fileMenu' } template.push({ label: 'ファイル', submenu: [isMac ? { role: 'close' } : { role: 'quit' }], }); // { role: 'editMenu' } const editSubMenu: Electron.MenuItemConstructorOptions[] = [ { role: 'undo' }, { role: 'redo' }, { type: 'separator' }, { role: 'cut' }, { role: 'copy' }, { role: 'paste' }, ]; if (isMac) { editSubMenu.push( ...([ { role: 'pasteAndMatchStyle' }, { role: 'delete' }, { role: 'selectAll' }, { type: 'separator' }, { label: 'Speech', submenu: [{ role: 'startSpeaking' }, { role: 'stopSpeaking' }], }, ] as Electron.MenuItemConstructorOptions[]) ); } else { editSubMenu.push( ...([ { role: 'delete' }, { type: 'separator' }, { role: 'selectAll' }, ] as Electron.MenuItemConstructorOptions[]) ); } template.push({ label: 'Edit', submenu: editSubMenu, }); // { role: 'viewMenu' } template.push({ label: 'View', submenu: [ { role: 'reload' }, { role: 'forceReload' }, { role: 'toggleDevTools' }, { type: 'separator' }, { role: 'resetZoom' }, { role: 'zoomIn' }, { role: 'zoomOut' }, { type: 'separator' }, { role: 'togglefullscreen' }, ], }); // { role: 'windowMenu' } const windowSubMenu: Electron.MenuItemConstructorOptions[] = [ { role: 'minimize' }, { role: 'zoom' }, ]; if (isMac) { windowSubMenu.push( ...([ { type: 'separator' }, { role: 'front' }, { type: 'separator' }, { role: 'window' }, ] as Electron.MenuItemConstructorOptions[]) ); } else { windowSubMenu.push({ role: 'close' }); } template.push({ label: 'Window', submenu: windowSubMenu, }); template.push({ role: 'help', submenu: [ { label: 'Learn More', click: async () => { const { shell } = require('electron'); await shell.openExternal('https://electronjs.org'); }, }, ], }); const menu = Menu.buildFromTemplate(template); Menu.setApplicationMenu(menu); いや何かもう少しいい方法が存在している気がしてならない。でもこれ以上思いつきませんでした。 何かいい案あれば教えてください。 それではー
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Node.jsにおける同期/非同期処理に関して

Node.jsの同期/非同期処理に関してのアウトプットするために本記事を投稿します。 マルチスレッド スレッドとはCPU利用の単位です。 CPUとは、Central Processing Unit(中央演算処理装置)の略で、プログラムの命令を解釈して、コンピューター全体の動作を制御する制御装置や演算を行う演算装置などの機能を持つ装置です。 近年のCPUでは、1つのプロセッサ・パッケージ内に複数のプロセッサ・コアを搭載するマルチコアプロセッサを使用しており、それに応じて複数のスレッドを同時に処理できるようになっています。(マルチスレッド) マルチスレッドを利用するとサーバーは複数のリクエストを受け取った際に、スレッドを増やして対応します。 しかし、リクエストが大量に届いた際にスレッドが多くなりすぎて、メモリの消費量が多くなったり、効率が悪くなるC10K問題(クライアント1万台問題)のような現象が起きる可能性があります。 Node.jsが採用しているシングルスレッドは、C10K問題を解消するのに有効です。 シングルスレッド 前章でも記載しましたが、Node.jsはシングルスレッドを採用しています。 シングルスレットとは、マルチスレッドと反対で1つのスレッドのみで処理を実行します。 シングルスレッドのメリット ・スレッドが1つであるため、C10K問題を解決できる シングルスレッドのデメリット ・スレッドが1つであるため、時間のかかる処理がある場合に、次の処理の待ち時間が増えてパフォーマンスが下がる可能性がある Node.jsでは、非同期 I/Oを用いてシングルスレッドのデメリットを解消します。 非同期I/O I/Oとは入出力処理入出力のことを指します。 非同期 I/Oでは、主にノンブロッキングI/Oを使用して非同期に処理をします。 例えば、時間のかかるAという処理の後にBという処理を実行するとします。 同期I/Oの場合は、Aの処理が終了した後に、Bの処理を実行します。 非同期I/Oでは、時間のかかる処理Aが終了する前に処理Bを実行して、時間を短縮することができます。 時間軸で処理を分散させて、1つのスレッドでも高速に処理を実行することができます。 ブロッキングとノンブロッキング ブロッキング ブロッキングとは、1つの処理が終了するまで、他の処理を実行しないことです。 「fs」というNodeパッケージの 「readFileSyncメソッド」などです。 ノンブロッキング ノンブロッキングとは、ブロッキングを行わず、非同期に処理を実行することです。 「fs」というNodeパッケージの 「readFileメソッド」などです。 具体例 「readFileSync」メソッドは、ファイルを読み込むブロッキングメソッドであり、同期I/Oを実現します。 「readFile」メソッドは、ファイルを読み込むノンブロッキングメソッドであり、非同期I/O実現します。 fs.readFileSync('./te.txt'); // ファイルが読み込まれるまでここでブロック console.log("ブロッキングメソッド") fs.readFile('./te.txt', (err, data) => { console.log("readFileメソッドを使用してファイルの読み込みが完了") }); console.log("ノンブロッキングメソッド") // 出力結果 ブロッキングメソッド ノンブロッキングメソッド readFileメソッドを使用してファイルの読み込みが完了 readFileSyncメソッドはブロッキングメソッドであるため、テキストファイルを読み込む処理が終了するまで、次の処理は実行されません。 readFileメソッドは、ノンロッキングメソッドであるため、非同期に次の処理が実行されています。 Node.jsでは、基本的に非同期I/Oを用いて処理を実行し、必要に応じてブロッキングメソッドやコールバック関数/Promise/async/awaitなどを使用して柔軟に同期/非同期処理を実行することができます。 awaitはブロッキングをしているのか function a(){ fs.readFileSync('./te.txt'); // ファイルが読み込まれるまでここでブロック console.log("ブロッキングメソッド") } async function b(){ return new Promise(function(resolve){ fs.readFile('./te.txt', (err, data) => { console.log("readFileメソッドを使用してファイルの読み込みが完了") resolve() }) console.log("readFileより先に処理されます") }); } (async ()=>{ a() await b() console.log("readFileより後に処理されます") })(); // 出力結果 ブロッキングメソッド readFileより先に処理されます readFileメソッドを使用してファイルの読み込みが完了 readFileより後に処理されます 上記のコードを作成してawaitがブロッキングを実行しているかを確認しました。 一見同期処理の様に見受けられますが、awaitはブロッキングをしているわけではありません。 以下、参考文献に記載しているページから参照しています。 await 式は返されたプロミスが履行されるか拒否されるまで実行を中断することで、プロミスを返す関数をあたかも同期しているかのように動作させます。 上記により、awaitは同期のような処理に見えますが、ブロッキングはしていないことがわかります。 参考文献 Node.js公式 https://nodejs.org/ja/about/ awaitに関して https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Statements/async_function
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Node.js] コマンドライン引数の取得

コマンドライン引数の取得 Node.jsプログラムからコマンドライン引数を取得するには、processオブジェクトのprocess.argvプロパティを参照する。processオブジェクトはnode.jsの実行環境のグローバル変数の1つであるため、モジュールをrequireで読み込む必要がない。また、process.argvは、文字列配列になっている。 sample.js for(let i = 0; i < process.argv.length; i++){ console.log('argv[' + i + '] = ' + process.argv[i]); } 上記の「sample.js」に引数を指定して実行すると以下の結果となる。 実行結果 $ node sample.js red green blue argv[0] = C:\Program Files\nodejs\node.exe argv[1] = C:\work\workspace\sample.js argv[2] = red argv[3] = green argv[4] = blue これより、argv[0]、argv[1]の要素にはnodeコマンドと実行されるスクリプトのファイルパス、argv[2]以降の要素にコマンドライン引数が格納されている。 「npm run」でのコマンドライン引数 さらに、「npm run」で実行する場合についてもあわせて紹介する。 まず、任意のスクリプトを「npm run」で実行する場合、package.jsonのscriptsプロパティでスクリプトを定義しておく必要がある。これにより「npm run <スクリプト名>」で実行できるようになる。 package.json "scripts":{ "sample": "node sample.js" } この場合、sample.jsの実行時に引数を渡す場合は以下のように、スクリプト名の後に引数を指定する。 $ npm run sample red green blue
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

指数バックオフ + ジッターをTypeScriptでやるだけ

導入 外部API叩く系のコード、テストで通ってもスケーリングしたら死ぬことがあります 単純に一回に大量に叩いたり、そうでなくてもエラーから復旧したりした時に全員が同期して叩くみたいなことがあると、それはもはやDDoS攻撃だからです それを解決するのが指数バックオフです ミスったらいい感じに待ってやり直すというアルゴリズムです その待つ時間まで一緒だと同期攻撃が繰り返されるだけなので乱数でずらします それがジッターです GCPのCloudStorageのドキュメントとかでも自分で実装しろと書いてあります 悲しい 本質 このアルゴリズムの本質は待ち時間です expBackoff.ts const MIN_OFFSET_MSEC = 2000 await sleep( (2 ** retries) * MIN_OFFSET_MSEC * ( 1 + Math.random() ) ) 今回は1回目は2〜4秒、2回目は4〜8秒、3回目は8〜16秒....の中で一様分布にしています 冪乗具合やrandom具合はお好みでアレンジしてください コード 関数と最大リトライ数を引数に、指数バックオフを実行する関数です expBackoff.ts const MIN_OFFSET_MSEC = 2000 // 最初は2秒からスタート、それ以降は倍倍で増える export const expBackOff = async (func: Function, MAX_RETRIES: number) => { let retries = 0 // やり直した回数 let retry = true // やり直すかフラグ let res while(retry && retries < MAX_RETRIES){ await sleep( (2 ** retries) * MIN_OFFSET_MSEC * ( 1 + Math.random() ) ) // 本質 try { res = await func() // undefinedは返さないで retry = false } catch (e) { console.log("retryCount: ", retries + 1) console.log("expbackoff ERROR: ", e) } retries += 1 } if(res == undefined) { throw new Error("Expbackoff failed. retryCount: " + retries) } return res } async function sleep(msec: number) { return new Promise(function(resolve) { setTimeout(function() {resolve()}, msec); }) } 使い方 index.ts const fetch = require('node-fetch'); const fetchFunc = async () => { // 指数バックオフさせたい関数を作る return await fetch(URL, options).then((response: any) => { // ちゃんとパースしてエラーに昇華させる if(isYokunai(response)){ // レスポンスがよくなかった時 throw new Error(response) } else { return response } }) // return await someGoodApi() // ちゃんとエラーハンドリングされるならこれだけでもよい } const response = await expBackOff(fetchFunc, 5).then((res: any) => { // 5回やり直す console.log("RESULT: ", res) return res }).catch((e) => { console.log("ERROR: ", e) return new Error(e) // 好きにハンドリングしてください }) まとめ 良いスケーリングライフを!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Node.jsの基本

はじめに Node.jsの基本を備忘録として残しておきます。 目次 Webアプリケーションの基本の3ステップ EJSテンプレートの使い方 ルーティングの基本 Webアプリケーションの基本の3ステップ requireでhttpオブジェクトを用意する。 const http = require('http'); createServerでサーバーを作る。 let server = http.createServer(ルーティング処理などを書いた関数); listenで待ち受け開始する。 server.listen(3000); EJSテンプレートの使い方 Webページの画面は基本的にEJSなどのテンプレートエンジンを利用します。 fsのreadFileSyncで読み込み、ejs.renderでレンダリングし、その結果をwriteで書き出す。 const fs = require('fs'); const ejs = require('ejs'); const index_page = fs.readFileSync('./index.ejs', 'utf8'); var content = ejs.render(index_page, { title: "Index", content: "これはIndexページです。", }); response.writeHead(200, { 'Content-Type': 'text/html' }); response.write(content); response.end(); break; ルーティングの基本 request.urlの値を取り出し、url.parseでパースして得られたオブジェクトから「pathname」を取り出し、その値に応じて処理を作成す。 const url = require('url'); let url_parts = url.parse(request.url); switch (url_parts.pathname) { case '/': var content = ejs.render(index_page, { title: "Index", content: "これはIndexページです。", }); response.writeHead(200, { 'Content-Type': 'text/html' }); response.write(content); response.end(); break; case '/other': ・・・略・・・ 全文コード const http = require('http'); const fs = require('fs'); const ejs = require('ejs'); const url = require('url'); const index_page = fs.readFileSync('./index.ejs', 'utf8'); const other_page = fs.readFileSync('./other.ejs', 'utf8'); const style_css = fs.readFileSync('./style.css', 'utf8'); var server = http.createServer(getFromClient); server.listen(3000); console.log('Server start!'); // ここまでメインプログラム========== // createServerの処理 function getFromClient(request, response) { var url_parts = url.parse(request.url); switch (url_parts.pathname) { case '/': var content = ejs.render(index_page, { title: "Index", content: "これはIndexページです。", }); response.writeHead(200, { 'Content-Type': 'text/html' }); response.write(content); response.end(); break; case '/other': //★追加 var content = ejs.render(other_page, { title: "Other", content: "これは新しく用意したページです。", }); response.writeHead(200, { 'Content-Type': 'text/html' }); response.write(content); response.end(); break; default: response.writeHead(200, { 'Content-Type': 'text/plain' }); response.end('no page...'); break; } }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ウェブページは作れませんでした 【RESTART】

ウェブページは作れませんでした(結論) 「progate」にて学習すること約1か月 「Web開発パス(Node.js)」を一通りこなしてみて とりあえず何かウェブページ作ってみるかと思った 結論、どうやって作ればいいのか解らず… 学習プロセスの見直し 「progate」だけ行ってしまうと自分の場合は インプットのみになってしまい、定着しないと反省 そこで以下のようなプロセスの仮説を立てて実行することにした 学習プロセスのフェーズ詳細 フェーズ1_学習 これまでと同じように「progate」のカリキュラムをこなすフェーズ フェーズ2_記録 「progate」で書いたコードをテキストファイル化して印刷、手書きで メモ等を加える(昭和生まれなのでアナログ手法) フェーズ3_実践 手書きメモを加えたテキストファイルを参考にして「Codepen」で作成する フェーズ4_発信 作成プロセスを言語化してQiitaの記事を作成する 人間は楽な方に流れる 計画を立ててプロセスを設定しないと どうしても楽なインプットだけに偏ってしまう それに気付かされた1か月だったと言える 過去記事
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Qiitaの記事の”勢い”を調べる ~もしかしたらまだ見ぬ掘り出し記事が見つかるかも~

配信者勢いランキング、5ちゃんねる勢いランキングなど、インターネットをうろうろしているとよく"勢い"という言葉を目にしますよね。 というわけでQiita記事の勢いを調べてみました。(QiitaAPIを使って) 参考になる記事をストックすることがスキルアップの近道のはず、"勢い"のある記事はきっと参考になる記事だと思うのです。 前調べ 5ちゃんねるの勢いの計算方法は次の通りだということを調べました。 勢い=レス数/(現在のunix時間-スレッド内の1番目の投稿のunix時間)÷86400 なるほど、これを参考に今回は、 ■レス数=いいねの数 ■現在の時間=現在の時間 ■1番目の投稿時間=記事の投稿時間 と定義してやってみます 結果 単月部門 集計期間:2021/9/13~2021/10/2 順位 いいね 投稿日 期間(日) 勢い URL 1 893 2021-09-29 15 61 エンジニア200人に聞いて、業務委託単価表を作りました 2 1090 2021-09-15 28 39 「あれ、プログラミングが楽しくない…」【すべてのエンジニアへ】 3 364 2021-10-03 10 35 仕事で使う技術以外はさっぱりわからないけど この先大丈夫かな……という方にオススメのLTイベントご紹介 4 41 2021-10-12 2 27 5,000円/時間以上で働く方法を考えてみた 5 226 2021-10-04 9 24 VueもReactもやったことないのでVanilla JSでやってみたSPA 6 577 2021-09-20 24 24 クリーンなReactプロジェクトの21のベストプラクティス 7 189 2021-10-05 9 22 プロダクトを作ってから登録ユーザー400名、月間売上100万円を達成するまでにエンジニアががむしゃらに行った営業方法のメモ書き 8 146 2021-10-06 7 21 過去にPLから言われた心に刻んでおきたい5つの言葉【要件定義編】 9 472 2021-09-18 25 19 【2021】モダンなPython開発環境の紹介 10 445 2021-09-19 25 18 Bash Scriptの作法 1位の記事は1日で61いいねを獲得したということになります。 この期間では突出した結果となりました。 記事を読んでみましたが、大変興味深くこの結果も納得のものでした。 続いて、期間指定なしの結果です 無差別級 集計期間:なし 順位 いいね数 投稿日 期間(日) 勢い URL 1 2537 2021-02-23 233 10.9 【2021 最新版】ノンデザイナーだからこそ知っておきたかった10のイラストサイト 2 4333 2021-05-24 507 8.5 Google社のテクニカルライティングの基礎教育資料がとても良かったので紹介したい 3 4687 2020-01-11 641 7.3 要件定義~システム設計ができる人材になれる記事 4 4363 2020-01-21 631 6.9 良いコードの書き方 5 3770 2020-03-08 584 6.5 プロジェクトリーダーというお仕事 6 2853 2020-04-16 545 5.2 VSCode に必ず入れておきたい拡張機能 7 3647 2019-11-11 702 5.2 2020年のフロントエンドマスターになりたければこの9プロジェクトを作れ 8 9757 2016-05-16 1976 4.9 ロシアの天才ハッカーによる【新人エンジニアサバイバルガイド】 9 5435 2018-09-30 1109 4.9 VSCodeのオススメ拡張機能 24 選 (とTipsをいくつか) 10 6241 2018-03-13 1310 4.8 AtCoder に登録したら次にやること ~ これだけ解けば十分闘える!過去問精選 10 問 ~ やはり、期間が延びると勢いも衰えていく結果となっています。 しかしながら1日10いいねは単月部門でもTOP20に入る勢いで、投稿後半年以上経過している中でいまだ衰えを知らないといっても過言ではないと思います。 とりあえずすべてストックに入れたのは言うまでもありません。 やった後に気づいたのですが、Qiitaのトレンドも似たようなものなので、結果は近しい内容になるかと思いましたがそうでもなかったです。 この記事だと、トレンドは打率で判断しているようでした。 Qiitaのトレンド表示機能についての考察 違う切り口で記事を拾ってみると、思わぬ掘り出し物が見つかるかもしれないですね。 ソースコードと開発のあれこれ 開発環境 Node v16.10.0 axios 0.22.0 最終的に出来上がったソースコードは次の通り。 qiita.rb const axios = require('axios'); var day = new Date(); var tday = new Date(); //今日 var bday = new Date(tday.setMonth(tday.getMonth()-1)); async function main() { let response = await axios.get( 'https://qiita.com/api/v2/items?page=1&per_page=100&query=stocks:>3000')  //'https://qiita.com/api/v2/items?page=1&per_page=100&query=created:>2021-09-13+stocks:>30') for (let i = 0; i < response.data.length; i++) { //response.data.length var xday = new Date(response.data[i].created_at); //86400000は1日、*24で時間、*60で分 var termDay = (day - xday) / 86400000; //1時間で1いいねを1勢いで計算 var ikioi = response.data[i].likes_count / termDay; console.log(i,response.data[i].likes_count,response.data[i].created_at,termDay,ikioi,response.data[i].url); } } main(); ↓便宜上、記事の取得はストック数で制限をかけています これにたどり着くまでが長かった。。。 検索時に利用できるオプション qiita.rb let response = await axios.get( 'https://qiita.com/api/v2/items?page=1&per_page=100&query=stocks:>3000')  //'https://qiita.com/api/v2/items?page=1&per_page=100&query=created:>2021-09-13+stocks:>30') 感想 手探りでのはじめてのjavascriptでしたが、思った以上に大変でした。 困ったらGoogle先生に聞く、Qiitaの先人たちの記事を見る等、解決策はあるのでしょうが、目当ての情報を得るまでに時間がかかる。調べる力も実力のうちだということを思い知った次第です。 ひとまずはエンジニアとして再出発です。(もう40手前ですが。。)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む