20220110のJavaScriptに関する記事は10件です。

Uncaught TypeError: Cannot read properties of null (reading 'addEventListener')

自分用メモ JavaScriptでボタンクリックのイベント発火させようとしたところ、consoleに以下のエラーが出た。 Uncaught TypeError: Cannot read properties of null (reading 'addEventListener') ちなみにその時のコードがこちら index.html <!doctype html> <html lang=”ja”> <head> <title>Index </title> <meta charse=”utf8″/> <script type="text/javascript" src="{{ url_for('static', filename='index.js') }}"></script> <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}"> </head> <body> <div class="input-area"> <input id="add-text" placeholder="TODOを入力" /> <button id="add-button">追加</button> </div> </body> </html> 原因は、scriptを読み込ませる位置だった模様。scriptを読み込ませる位置を一番後ろに持ってくるとエラー解消した。 index.html <!doctype html> <html lang=”ja”> <head> <title>Index </title> <meta charse=”utf8″/> </head> <body> <div class="input-area"> <input id="add-text" placeholder="TODOを入力" /> <button id="add-button">追加</button> </div> </body> </html> <script type="text/javascript" src="{{ url_for('static', filename='index.js') }}"></script> <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vuexはもう古い!?これからの状態管理はPiniaを使おう

はじめに 煽り気味のタイトルですみません。Vuexは使わない方がいいという話ではなく、別の有力な選択肢が出てきたというレベルの話であると最初に断っておきます。 みなさん、Piniaという状態管理ライブラリをご存知ですか?僕は昨日知りました。 調べてみるとどうやら先月Vueの公式リポジトリ入りをしており、今後さらに注目が集まるライブラリになりそうです。 * ちなみにPiniaはVuexを置き換えるものではなく、Vuexは今後も開発を継続していくようです(現在はVuex5を開発中)。 Vueの状態管理といえばVuexというイメージがありますが、TypeScriptとの相性が良くないだとか、そもそも冗長だとかで長らくVue3時代の状態管理のベストプラクティスがよくわからない状況が続いていました(?) 僕の知っている限りではVue状態管理の方法は 1. Vuex 2. provide/inject 3. 単にグローバルでリアクティブ変数を宣言する 4. Pinia の4つですが、1の問題は前述した通りで、2、3はシンプルですがプロダクトやチームの規模によっては無秩序になりやすいという問題があります。 その点Piniaはシンプルで型安全かつパフォーマンスも良いというので、かなり有力な候補になり得るのではないでしょうか。 目次 基本的な使い方 補足 参考文献 基本的な使い方 導入は簡単 公式より yarn add pinia main.ts import { createPinia } from 'pinia' // ... app.use(createPinia()) これだけ ストアはdefineStoreで定義 ぱっと見そんなにVuexと変わらないので、Vuex利用者は特に説明がなくても理解できると思います。 第一引数はアプリケーション内でユニークなキー 第二引数はストアの定義となるオブジェクトで、基本はstate,getters,actionsの3つのプロパティを指定 vuexと違いmutationsが存在しないのでよりシンプルですね stores/counter.ts import { defineStore } from 'pinia' export const useCounterStore = defineStore('counter', { state: () => { return { count: 0, } }, getters: { double: (state) => state.count * 2, }, actions: { increment() { this.count++ }, } }) 使用する際は <script lang="ts"> import { defineComponent } from 'vue' import { useCounterStore } from '@/stores/counter' import { storeToRefs } from 'pinia' export default defineComponent({ setup() { const counterStore = useCounterStore() // toRefsだとreactivityが壊れるので注意 const { count } = storeToRefs(counterStore) return { counterStore, count } }, }) </script> <template> <div>{{ count }}</div> <div>{{ counterStore.double }}</div> <button @click="counterStore.increment()" > increment! </button> </template> <script setup>記法で書くと ※ この記法を知らない方はここを参照 <script setup lang="ts"> import { useCounterStore } from '@/stores/counter' import { storeToRefs } from 'pinia' const counterStore = useCounterStore() const { count } = storeToRefs(counterStore) </script> <template> <div>{{ count }}</div> <div>{{ counterStore.double }}</div> <button @click="counterStore.increment()" > increment! </button> </template> 基本的な使用方法は以上です。 storeToRefsでリアクティブを維持したまま分割代入可能です。 gettersプロパティはcomputedと同様にリアクティブです。 おまじないが少なくて作業中に頭が混乱するのを防げそうですね。 補完もバッチリ効きます。 ちなみに、store.count++ とかstore.count = 10とかも可能です 実態はreactive()で作成したリアクティブオブジェクトのラッパーにすぎないのでこういうこともできちゃいます。 実際の運用ではstateを直接変更するのは禁止すべきかもしれませんね。 僕の場合、個人開発ではpiniaっぽいことをわざわざライブラリ無しでやっていたので、まさに自分が求めていたものが既に存在していたと知り感動しました。 補足 他のstoreにアクセスしたい場合 この場合もシンプルで、単に使用箇所でストアを取得しプロパティにアクセスするだけです。 stores/counter.ts import { useOtherStore } from './otherStore' export const useCounterStore = defineStore('counter', { state: () => { return { count: 0, } }, getters: { other: (state) => { const other = useOtherStore() return other.num * state.count } }, }) 参考文献 - 公式サイト
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

いまさら理解する jQuery Deferred

概要 現代の JavaScript における Promise と jQuery Promise では、非同期処理の振る舞いが異なることを示します。また、混同されがちな jQuery の then と done/fail/always の使い分けについて解説します。 この記事のすべてのコードは、以下のページで試すことができます。 https://at6ue.github.io/jquery_deferred_demo/ jQuery の Deferred と Promise Deferred Object は jQuery において主に非同期処理を扱うための仕組みです。例えば、以下のように非同期処理の完了を待って then に登録したコールバックを実行させることができます。 01.js const fn = () => { const deferred = $.Deferred(); setTimeout(() => { console.log("resolve"); deferred.resolve("1st"); }); return deferred.promise(); }; const promise = fn(); const ret = promise .then((first) => { console.log(`${first} then`); return "2nd"; }) .then((second) => { console.log(`${second} then`); }); console.log("start"); /** * start * resolve * 1st then * 2nd then */ 非同期処理といっても JavaScript はシングルスレッドで実行されるため、コード断片が同時に実行されることはありません。あるタスクが完了したときに呼び出されるコールバックを登録できるだけです。 jQueryPromise は、初期状態の pending (待機)と、決定された resolved (解決)と rejected (拒否)のいずれかの状態を持ち、状態がいずれかに決定されたときに予め登録されたコールバックを呼び出す仕組みです。 ところで上の例では、 非同期関数 fn は jQueryPromise を返しています。jQueryDeferred 自身も jQueryPromise と同じように、 then などを使って非同期処理の完了後に呼び出されるコールバックを登録できます。しかし、特別な理由がない限り非同期関数は jQueryPromise を返すべきです。 以下は、 jQueryDeferred を返す非同期関数 fn の外側で状態が決定されてしまった例です。 02.js const fn = () => { const deferred = $.Deferred(); setTimeout(() => { console.log("(won't be resolved)"); deferred.resolve("resolved"); }); return deferred; }; const deferred = fn(); deferred .done((resolved) => { console.log(resolved); }) .fail((rejected) => { console.log(rejected); }); deferred.reject("rejected"); /** * rejected * (won't be resolved) */ done と fail はそれぞれ状態が resolved と rejected に決定されると呼び出されるコールバックを登録する関数です。この例では、非同期関数 fn の外側で状態が rejected に決定されてしまったため、 fn の内側で resolve を呼び出しても done に登録したコールバックは呼び出されません。jQueryPromise は最初に決定された状態を保ち、その後に呼び出された resolve/reject は無視します。1 jQueryDeferred とは異なり、jQueryPromise は resolve や reject を持たないので、関数の返値を jQueryPromise にしておけば、関数の外側で状態が決定される心配はありません。 then と done/fail/always さきほどの例では fail を使って、状態が rejected に決定されると呼び出されるコールバックを登録しましたが、 then の第 2 引数も状態が rejected に決定されると呼び出されるコールバックを受け付けます。 jQuery 1.8 以降の then は以下のように定義されています。 deferred.then( doneFilter [, failFilter ] [, progressFilter ] ) then と done、fail および always は似たような目的で使われますが、その役割は大きく異なります。then は新しい jQueryPromise を返すのに対して、done/fail/always は自分自身を返します。 03.js const deferred = $.Deferred(); const promise = deferred.promise(); const promiseDone = promise.done(() => {}); const promiseThen = promise.then(() => {}); console.log(`done returns ${promise === promiseDone ? "same" : "new"} promise`); console.log(`then returns ${promise === promiseThen ? "same" : "new"} promise`); /** * done returns same promise * then returns new promise */ つまり、then が返す新しい jQueryPromise は、元のオブジェクトとは異なる状態に決定できます。これは、任意状態に決定した jQueryPromise を返すコールバックを then に登録することで実現できます。 以下は、rejected に決定された jQueryPromise を受けて、resolved に決定された jQueryPromise を返す例です。 04.js const firstArg = "1st arg"; const secondArg = "2nd arg"; const deferred = $.Deferred(); setTimeout(() => { deferred.reject(firstArg); // A }); deferred .promise() .fail((arg) => { // B console.log(arg); return secondArg; }) .then( () => {}, (arg) => { // C console.log(arg); return $.Deferred().resolve(secondArg).promise(); } ) .done((arg) => { // D console.log(arg); }); /** * 1st arg * 1st arg * 2nd arg */ A で jQueryDeferred が rejected に決定されます。ここで rejected 状態のために登録された B と C のコールバックが登録された順に呼ばれます。 C のコールバックが resolved に決定された jQueryDeferred を返したため、これに続く resolved 状態のために登録された D のコールバックが呼ばれます。 このように、then に登録したコールバックの返値が、then の返値である新しい jQueryPromise に反映されていることが分かります。 一方、B で fail に渡されたコールバックの返値は使われていません。done/fail/always は自分自身を返すため、登録されたコールバックの返値を後続のメソッドチェインに渡せないのです。これは done/fail/always に登録したコールバックが非同期処理を含む場合、後続するメソッドチェインではその完了を待機できないことを意味しています。 以下は、done に登録したコールバックのなかで、非同期的にリソースを読み込んでいる例です。 05.js const deferred = $.Deferred(); setTimeout(() => { deferred.resolve(); }); deferred .promise() .done(() => { console.log("1st done"); return $.get("resource").then((data) => console.log(`done: ${data}`)); }) .done(() => { console.log("2nd done"); }) .then(() => { console.log("then"); return $.get("resource").then((data) => console.log(`then: ${data}`)); }) .done(() => { console.log("3rd done"); }); /** * 1st done * 2nd done * then * done: data retrieved * then: data retrieved * 3rd done */ 最初の done に渡されたコールバックによる非同期処理の完了を待たず、即座に 2 つ目の done に渡されたコールバックが実行されています。 一方、3 つ目の done に渡されたコールバックは、then に渡されたコールバックの非同期処理の完了を待って実行されています。 このように、done/fail/always は then の省略形ではなく2、状態の決定に伴って即座に実行されるべき同期的な処理を登録するための関数といえます。 Promise との比較 jQuery 3.x では then の仕様が Promises/A+ に基づくように変更されました。これは、ES6 で導入された Promise と互換性をもつためですが、jQueryPromise の then と Promise の then の挙動は同じではありません。 まず、jQuery 2.x までと jQuery 3.x の then の実行順を比べてみましょう。 06.js const deferred = $.Deferred(); setTimeout(() => { deferred.resolve(); console.log("resolved"); }); deferred .then(() => { console.log("1st then"); setTimeout(() => console.log("1st timeout")); }) .then(() => { console.log("2nd then"); setTimeout(() => console.log("2nd timeout")); }); /** * jQuery <2.x * --- * 1st then * 2nd then * resolved * 1st timeout * 2nd timeout * * jQuery 3.x * --- * resolved * 1st then * 1st timeout * 2nd then * 2nd timeout */ jQuery 2.x までは resolve や reject が呼び出される、つまり状態が決定されると即座に then や done/fail/always に登録されたコールバックを実行していました。3 jQuery 3.x はその仕組みを引き継ぎつつ、then に登録されたコールバックだけは setTimeout に渡して実行します。 setTimeout に登録されたコールバックは指定時間後にタスクキューに追加され、イベントループの最初に実行されます。そのため、上記の例を jQuery 3.x で実行すると、まず同期的なコード断片がすべて実行され、次に then と setTimeout に登録されたコールバックが順に実行されます。4 同様のコードを Promise で試してみましょう。 07.js const promise = new Promise((resolve) => { setTimeout(() => { resolve(); console.log("resolved"); }); }); promise .then(() => { console.log("1st then"); setTimeout(() => console.log("1st timeout")); }) .then(() => { console.log("2nd then"); setTimeout(() => console.log("2nd timeout")); }); /** * resolved * 1st then * 2nd then * 1st timeout * 2nd timeout */ Promise では then に登録されたコールバックが先に呼び出され、次に setTimeout に登録されたコールバックが呼び出されています。Promise は then に登録されたコールバックをマイクロタスクキューに追加します。マイクロタスクは現在実行中のタスクが完了した直後に実行されるため、setTimeout に登録したコールバックよりも優先されます。 jQuery では then や done/fail/always は渡されたコールバックに Callbacks Object に追加していますが、この内部実装は配列です。そのため、これらのコールバックは resolve/reject を呼び出したタスクの一部として即座に実行されます。 まとめ jQueryDeferred は resolve/reject 関数をもち、状態を決定できる jQueryPromise は初期状態の pending 、および決定された resolved と rejected の状態をもつが、自身では状態を決定できない done/fail/always は resolved/rejected のうち対応する状態に割り当てられたキューにコールバックを追加し、自分自身を返す関数 then は resolved/rejected のそれぞれの状態に割り当てられたキューにコールバックを追加し、そのコールバックの返値で決定される新しい jQueryPromise を返す関数 jQuery 3.x では、then に渡されたコールバックは setTimeout を介してタスクキューに追加されるため、コールスタックが空になった後に遅延実行される ES6 の Promise における then は、渡されたコールバックをマイクロタスクキューに追加するため、jQueryPromise とは挙動が異なる "Once the Deferred has been resolved or rejected it stays in that state; a second call to deferred.resolve(), for example, is ignored." -- jQuery.Deferred() ↩ これとは対照に、Promise.prototype.catch() は then を内部的に呼び出しており省略形といえます。 ↩ この実装は Promises/A+ の基となった Promises/A に相当するようです。 ↩ jQuery 2.x までの then と同じ実装は jQuery 3.x では pipe に残されています。 ↩
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

qittaの記事を見てぱっと見謎だったfilter, reduceに渡すアロー関数を理解した

qittaの記事を見てぱっと見謎だったソースが以下。 index.ts function sumOfPos(arr: number[]): number { return arr.filter(num => num >= 0).reduce((acc, num) => acc + num, 0); } まず以下の部分 index.js arr.filter(num => num >= 0) javascriptのfilter関数の公式を見てみると index.js let newArray = arr.filter(callback(element[, index, [array]])[, thisArg]) そうかfilter関数の引数にコールバック関数を渡すのか。 とすると無名関数を渡しているので以下と同義。 index.js arr.filter(function(num){ return num >= 0; }); 続いて以下の部分 index.js reduce((acc, num) => acc + num, 0) これもjavascriptのreduce関数の公式をみると 第一引数にコールバック関数を渡すみたい。なので、 (第2引数は初期値) index.js reduce(function(acc, num){ return acc + num; }, 0); つまり index.ts const data: number[] = [-1, 5, -3, 4, -2, 5]; function sumOfPos(arr: number[]): number { return arr.filter(num => num >= 0).reduce((acc, num) => acc + num, 0); } console.log(sumOfPos(data)); //5 + 4 + 5 = 14
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

語彙診断をより簡単に

はじめに 皆様はhttp://testyourvocab.com/ というサイトをご存知でしょうか。 以下のような単語リストにチェックを付けていくと、今の語彙レベルが分かるというサイトです。 問題点 一括選択がない... そう、このサイトにはすべての単語を一括で選ぶことはできません。 そこで  簡単なScriptを組みました。 document.querySelectorAll("input[type='checkbox']").forEach(function(v){v.checked = true});  あとは開発モードを開いてこれを実行するだけです。 全部チェックが付きました。これで良いはず。 おわりに awryやmaladroitなど明らかに見覚えのない単語も入っていますがおそらく大丈夫でしょう。 最後までご覧いただきありがとうございました。いいなと思ったらLGTMやコメントをいただけると幸いです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PDFをブラウザ全画面モードにしてスライドショーができる、ビデオ会議で便利かもしれないツールを作ってみた

概要 Webブラウザで、手元のコンピュータの中にあるPDFを1つのタブのなかでスライドショー的に投影するためのツールを作ってみました。 デモページはgithub.ioのこちらからどうぞ。 リポジトリはGitHubのこちら。 メニューはこんな感じです。 PDFレンダラはPDF.jsのサンプルそのまんまで、キーボード操作のあたりだけをメインに足しました。 動機 ブラウザでPDFを見るときに、ツールバーがないものを作ってみたかったので。 利用シーンとしては、以下のようなものです。 特にビデオ会議での話なのですが、他人に自分の画面共有をしたいときで、デスクトップ全体ではなく特定のウインドウだけ見せたい、というシーンがあると思います。 1つのブラウザの画面で、関連資料と、WEBページ上の何か(オンラインのVSCodeとか)をタブで開き、フルスクリーン(WindowsだとF11とかで、macOSだとFn + Fとかで)にして、相互に(Ctrl+TABとかのショートカットで)移動できると、画面がしゅっと切り替えられてかっこいい 切り替えた先が、フルスクリーンのPDF資料になっているとかっこいい 上記で、3.だけ、2022年1月時点で、自分が使っているブラウザ(Chrome, Firefox, Safari)では実現できなさそうだったので、 PDF.js を使わせてもらって作れないかなと思いました。 実装 使ったもの 実行時のライブラリで依存したものは、以下の2つです。 - PDF.js - lodash jQueryも、気の利いたロギングライブラリも使いませんでした。 ページのレンダリング部分 ブラウザのウインドウのサイズに合わせて画面いっぱいにPDFの1ページを描くようにしました。 PDF.jsのコードサンプルのページを元に、元のPDFの縦横を見て、ぴちっと描けるようにしてみたものです。 MacとWindows10 PCで動かしてみて大丈夫そうだったので、これでいいやということにしています。 iOSやiPad OS、Androidでは試していません。 pageRendering = true // Using promise to fetch the page pdfDoc.getPage(num).then(function (page) { // treat scaling debug('window.devicePixelRatio' + window.devicePixelRatio) const desiredWidth = document.documentElement.clientWidth const desiredHeight = document.documentElement.clientHeight debug('desiredWidth: ' + desiredWidth + ' desiredHeight: ' + desiredHeight) const viewport = page.getViewport({ scale: 1 }) const scaleW = desiredWidth / viewport.width const scaleH = desiredHeight / viewport.height const scale = Math.min(scaleW, scaleH) debug('scale:' + scale) const scaledViewport = page.getViewport({ scale: scale }) debug('viewport: ' + viewport.width + ' x ' + viewport.height) debug('scaled: ' + scaledViewport.width + ' x ' + scaledViewport.height) canvas.height = Math.floor(scaledViewport.height) canvas.width = Math.floor(scaledViewport.width) // Render PDF page into canvas context const renderContext = { canvasContext: ctx, viewport: scaledViewport } const renderTask = page.render(renderContext) // Wait for rendering to finish renderTask.promise.then(function () { pageRendering = false if (pageNumPending !== null) { // New page rendering is pending renderPage(pageNumPending) pageNumPending = null } }) }) updatePageNumLabel(num) } キーボードでの操作 ページ移動 画面には操作ボタンを出さないので、ページの移動はキーボードでの操作になります。 Unixのlessコマンドに準じて、"SPACE"(か "f")で次のページ、"b"で前のページにしました。先頭"p"と末尾"G"へのジャンプも付けました。 (lessだと"G"なのですが、大文字小文字でなくキーだけ見ているので、"g"でも同じ効果) ブラウザ側にはなるべくキーイベントを受け取ってほしくないので、event.preventDefault()を呼ぶようにしたのですが、「F」キーだけ、macOSでの「最大化(Fn + F)」とかぶっており、かつ、JavaScriptのKeyEventだけではどうにもならなさそうだったので、 /** * handles "less" command style short cuts * * @param {*} evt * @returns true if event is handled in this function, otherwise false */ function handleLessStyleShortCut (evt) { const key = evt.keyCode || evt.charCode || 0 debug(key, evt.metaKey, evt.keyCode, evt.charCode) switch (evt.code) { case 'KeyB': { evt.preventDefault() onPrevPage() break } case 'KeyF': { if (!shouldPassThroughKeyEvent(evt)) { evt.preventDefault() } onNextPage() return true } case 'Space': { evt.preventDefault() onNextPage() break } case 'KeyP': { evt.preventDefault() pageNum = 1 queueRenderPage(pageNum) break } case 'KeyG': { evt.preventDefault() pageNum = pdfDoc.numPages queueRenderPage(pageNum) break } case 'KeyR': { evt.preventDefault() queueRenderPage(pageNum) break } default: { // do nothing return false } } return true } ページの前後以外 以下も付けました。 メニューを出したり消したり「.」 ファイルを開く「o」 画面再描画「r」 (リサイズうまくいくようにしたので、使うことはないと思いますが) 数字キーでページを入れて、「Enter」を押すと指定したページにジャンプ そのほか見た目 ファイルをロードしたあとは、ウインドウのタイトルにファイル名を反映させています。 PDFを移し始めると操作メニューは消すので、メニュー画面の出し方が画面に出ていないのをどうしよう、、、と思いましたが、プログラマであればきっと「.」も押すに違いない、なぜならばGitHubリポジトリでそのままVSCodeを開くショートカットも「.」だし、という謎の理由により、よしとしました。 動作 方法1. デモサイト、github.io上にあります。 方法2. GitHubのリポジトリをクローンし、お手元でWEBサーバを上げるツール( VSCodeの機能拡張のLive Serverや、 Web Servrer for Chrome )を使ってサーバを上げ、ブラウザから使えます。 テスト 手動で数パターンやっただけです。PDFファイルは、46ページ、3MBytes程度のもので確認しています。 画面設定、OS側でのスケーリングを2種類ずつためして、くずれないであろうところまでは確認しました。2022年1月時点で自分が使いそうなものについては動いたのでよしとします。 OS Browser Display Settings Windows10 21H1 Firefox 95.0.2 100%, 125% Windows10 21H1 Chrome 97.0.4692.71 100%, 125%) macOS 12.0.1 (Monterey) Safari 15.1 normal, expand space 注意:Windows環境で、画面のスケーリングを変更した後は、設定画面上にも注意がある通り、ブラウザの再起動が必要になることがあります。少なくとも2022年1月の筆者の環境では、ブラウザの再起動が必要でした。 おわりに PDF.jsがとてもよくできているので、特に難しいことはしなくてもサクッとPDFがブラウザで描けて楽しかったです。 future work もしかすると、キーボードのショートカットで、矢印キーでの移動も付けておくとよかったかも。。と思いました。誰かからつけてほしいと言われたら機能つけることにします。 PDF.jsは、リモートにあるファイルも取り扱ってくれるのですが、異なるサイトからのPDFのロードでは、提供側でCORS対応のヘッダーつけてくれていないといけない、という縛りがあります。対応していないサイトからダウンロードしようとして「エラーがでるんだが。。」と言われても困ってしまうので、意図して機能を落としました。これは「知っている人モード」をつけたうえで、有効化してもよいかもしれません。 メニューが若干しょぼいので見た目はちょっと変えたいかも。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

DenoであらゆるPythonライブラリが使えるようになったらしいので試す

Denoからあらゆるpythonライブラリを呼び出すことができる、その名も「python」というライブラリが公開されました。 これは面白そう…!ということで、使ってみようと思います。 使い方 1. Pythonのインストール 最初にPythonをインストールする必要があります。 自分の場合は最初からPythonが入っていたので、この手順は必要ありませんでした。 Pythonが入っていない人は適宜ググってください。 2. Pythonを呼び出すJSコードの作成 まずはfizzbazzを実行してみます。コードは以下のような感じになりました。 import { python } from "https://deno.land/x/python@0.1.0/mod.ts"; // `python.runModule`メソッドは、引数のpythonコードをモジュールとして実行する const fizzbuzzModule = python.runModule(` def fizzbuzz(len): return [ "fizz" if i%6==0 else "buzz" if i%3==0 else "fizzfizz" if i%2==0 else "bazzbazz" for i in range(len) ] `); // `fizzbuzz(10)`の結果はPythonオブジェクトのラッパー // JSで使える値に変換するには`.valueOf()`メソッドを呼ぶ for (const i of fizzbuzzModule.fizzbuzz(10).valueOf()) { console.log(i); } ポイントは、 pythonのコードをpython.runModuleに与えて実行する python.runModuleの返り値はimportしたpythonモジュールのように扱える pythonモジュールからimportした値や、pythonの関数の返り値をJSで扱うにはvalueOf()メソッドを呼ぶ valueOf()自体はJSが暗黙の型変換で呼び出すことが多いので、手動で呼ぶことは少ないかも 3. 実行する 実行するのですが、要求されるパーミッションが大きく、--allow-env/--allow-run/--allow-read/--allow-ffi/--unstableフラグが必要です。 使用するPythonのdllファイルを指定するために、環境変数DENO_PYTHON_PATHを指定します。この変数は指定しなくてもいいのですが、指定しない場合は色々なフォルダを読みに行ってdllファイルを探すようで、その場合--allow-readフラグで大量のフォルダを指定する必要があります。 セキュリティ的にもよくないので、DENO_PYTHON_PATHを指定してしまったほうがよさそうです。 以下は自分の環境の場合を示します。DENO_PYTHON_PATHは各自環境に合わせた値を設定してください 実行(windowsのコマンドプロンプトの場合) # pythonのdllファイルへのパスを環境変数に設定 > set DENO_PYTHON_PATH=C:/Users/azusa/AppData/Local/Programs/Python/Python38/python38.dll # 実行 > deno run --unstable --allow-env=DENO_PYTHON_PATH --allow-read=DENO_PYTHON_PATH --allow-ffi ./test.ts output fizz bazzbazz fizzfizz buzz fizzfizz bazzbazz fizz bazzbazz fizzfizz buzz 無事、fizzbazzできました。 pythonコードに日本語が含まれているとエラーが出ました。 日本語が含まれているとコードの一部が欠けた状態で実行される?ことがあるようで、奇妙なエラーが発生します。 今後のアップデートで修正されることを期待しましょう。 自分の場合はエディタのプラグイン経由で実行した時に標準出力が受け取れない事がありました。自分の環境に問題があるのかもしれませんが、python内でprint関数を使うときには正しく出力されるか確認したほうがいいかもしれません。 Pythonモジュールを使う pythonモジュールを直接importすることもできます。 import { python } from "https://deno.land/x/python@0.1.0/mod.ts"; const { print, str } = python.builtins; const { version } = python.import("sys"); print(str("Hello, World!").lower()); print(`Python version: ${version}`); > deno run --unstable --allow-env=DENO_PYTHON_PATH --allow-read=DENO_PYTHON_PATH --allow-ffi ./private_check.ts hello, world! Python version: 3.8.6 (tags/v3.8.6:db45529, Sep 23 2020, 15:52:53) [MSC v.1927 64 bit (AMD64)] 公式expamleによると、numpyやmatplotlibなども動作するようです。 import { python } from "https://deno.land/x/python@0.1.0/mod.ts"; const np = python.import("numpy"); const plt = python.import("matplotlib.pyplot"); const xpoints = np.array([1, 8]); const ypoints = np.array([3, 10]); plt.plot(xpoints, ypoints); plt.show(); しかし、これを実行したところエラーが発生しました。 error: Uncaught (in promise) PythonError: No module named 'numpy' throw new PythonError(errorMessage); ^ at maybeThrowError (https://deno.land/x/python@0.1.0/src/python.ts:641:9) at Python.importObject (https://deno.land/x/python@0.1.0/src/python.ts:717:7) at Python.import (https://deno.land/x/python@0.1.0/src/python.ts:727:17) 詳しく検証したいところですが、眠いので寝たいと思います おやすみなさい~
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScript ライジングスター 2021

2021 / 2020 / 2019 JavaScriptライブラリのトレンドを紹介しているbestofjs.orgが、2021年に最もホットであったJavaScriptライブラリのランキングを発表しました。 選考基準は累計スター数ではなく、『2021年の一年間で増えたスターの数』です。 過去流行っていたけど落ち目となった技術は出てこないので、最近注目されている技術がわかります。 ちなみに2016年の総合ランキング1位はVue.js、2017年の総合ランキング1位はVue.js、2018年の総合ランキング1位はVue.js、2019年の総合ランキング1位はVue.js、2020年の総合ランキングはDenoです。 以下は2021年のランキング、2021 JavaScript Rising Starsの日本語訳です。 JavaScript ライジングスター 2021 6回目のJavaScript ライジングスターにようこそ! JavaScript疲れから逃れるためにここに来たみなさん、ちょうどいいところに来ましたね。 今回はメタフレームワーク、速度の必要性、界隈のオールスターがテック企業にジョインした話などの話題があります。 しかし、まずはチャンピオンの話です。 今年は誰もが予想していなかった新しい覇者が誕生しました! しかもそれは、なんとコマンドラインツールです! 以下は、2020年の1年間で増加したGitHubのスター数によるランキングです。 Webプラットフォームベストプロジェクトを集めたリストであるBest of JSからの一年間の分析です。 なお、プロジェクトをクリックすると、より詳細な情報を見ることができます。 総合ランキング 1位: zx 2位: Vite 3位: Next.js 4位: React 5位: Tauri 6位: Tailwind CSS 7位: VS Code 8位: Slidev 9位: NocoDB 10位: Vue.js トップ3 2021年、最もトレンディだったプロジェクトは、Googleによる全く新しいツールです。 JavaScriptやTypeScriptでシンプルなコマンドラインスクリプトを書くことができるzxです。 基本的な使い方としては、コードにbashコマンド(ls・cat・gitなど…なんでも!)を埋め込むことができ、さらにそれをテンプレートリテラルでawaitすることもできます。 また、有名なパッケージも幾つか含まれています。 ・node-fetch ブラウザのfetch APIと同じことができる。 ・fs-extra ファイルシステムの操作ができる。 ・globby ユーザフレンドリーなパターンマッチングができるglob。 2位はViteです。 これはesbuildコンパイラを使用し、優れたパフォーマンスを示すビルドツールです。 当初はVue.js専用として誕生しましたが、現在ではReact・Svelte・Litなど主なフレームワークと連携が可能です。 3位のNext.jsは、Reactをリードするメタフレームワークとしての地位を盤石なものにしています。 Tauri 5位に入ったTauriは、Web技術を用いてデスクトップアプリケーションを構築するソリューションです。 Electronと比べると、Rustで書かれており、実行環境にNode.jsが必要ないのが特徴です。 5月にバージョン1.0のベータ版がリリースされました。 ピックアップ トップ10には入っていませんが、Astroは今年よく注目されたプロジェクトのひとつです。 より少ないJavaScriptで、より高速なローディングを実現するツールです。 コンセプトはSSGに近いですが、Astroではislandsと呼ばれる動的な部分をページに含めることができるところが特徴です。 また、クライアントサイドで動的コンポーネントをレンダリングする際に、複数の戦略を取ることができます。 ・ページが読み込まれたとき ・アイドル状態のときに、優先順位の低いコンポーネントを読み込む ・Intersection Observer APIを使用した監視 そしてAstro最大の特徴は、React・Vue.js・Svelteなど、あらゆるフレームワークとの組み合わせが可能ということです。 フロントエンドフレームワーク 1位: React +18.8k 2位: Vue.js +14.3k 3位: Svelte +13.6k 4位: Angular +9.3k 5位: Solid +8.5k 6位: Alpine.js +6.8k 7位: vue-next +6.7k 8位: petite-vue +5.5k 9位: Lit +3.0k 10位: htmx +2.7k JavaScript Rising Starsが始まって以来初めて、ReactがVueを退け、フレームワークの頂点に立ちました。 とはいえ、Vueはバージョン2(Vue.js)とバージョン3(vue-next)の2リポジトリに分かれているため、実質的にはVueが1位といってもいいでしょう。 大きな変化としては、SvelteがAngularを下して台頭してきたことです。 Viteなどを始め、ターゲットフレームワークにSvelteを含めるツールやコンポーネントも増加しています。 大きな話題のひとつとして、Svelteの開発者であるRich Harris氏が、Next.jsを提供しているVercelに入社したことが挙げられるでしょう。 Svelteも、Next.jsのようなアプリ構築のためのメタフレームワーク、SvelteKitを持っています。 5位に入ったSolidは、Reactに替わる選択肢として興味深いものです。 コンポーネントはReactのようにJSXで記載しますが、しかしVirtual DOMには依存しません。 SolidはMitosisにも影響を与えました。 これは、コンポーネントをあらゆるフレームワーク向けにコンパイル可能にしようという野心的なプロジェクトです。 Node.jsフレームワーク 1位: Next.js +19.7k 2位: Nest +10.1k 3位: Strapi +9.6k 4位: Remix +9.4k 5位: Nuxt +6.2k 6位: SvelteKit +6.2k 7位: Fastify +4.8k 8位: Blitz +4.5k 9位: Redwood +4.5k 10位: Express +4.1k 主なUIフレームワークは、モダンでスケーラブルなアプリケーションをビルドするための独自メタフレームワークを持っています。 これらはルーティング、サーバサイドレンダリング、SSG、最適化ビルドなどの機能を提供しています。 ・Reactにはこの分野のパイオニアと言えるNext.jsがあり、そしてカテゴリの勝者である。 ・VueにはNuxtがあるが、バージョン2用とバージョン3用で別物となっている。 ・SvelteにはSvelteKitがある。 2021年に登場した新参者であるRemixは、Reactアプリを構築するフルスタックフレームワークであり、そして一番の話題作です。 このプロジェクトはReact Routerの作者による新作で、10月までは有料サポーターのみが利用可能でした。 公開後も多くの支持を得続けており、ファンドで300万ドルもの資金を獲得しました。 プロジェクトのモットーは、Webの基礎、モダンなUXと明確であり、APIはできるかぎりWeb標準に準拠しています。 私が気に入った2つの例を紹介したいと思います。 フォームを送信するには……フォームを送信すればいい。 当たり前のことにしか見えませんが、しかし開発者はフォーム送信を避けるためにevent.preventDefault()を書くことに慣れきってしまっています。 それに、JavaScriptが無効な環境でもフォームを送信できたほうがいいに決まっています。 Remixは、我々が当たり前だと思っていたことに挑戦し、ユーザと開発者双方の体験を重視した新しいアプローチで、古い原則を"remix"しています。 もうひとつ、Remixはネストしたルーティングを非常にうまく処理することができます。 無限のHTTPリクエストを生成して画面をローディング中で埋め尽くすことなく、必要なコンポーネントの必要とするデータだけを効率的に読み込むことができます。 2位のNestは、特定のフレームワークに縛られないオーソドックスな、サーバサイドNode.jsフレームワークの王者です。 3位のStrapiはヘッドレスCMSのトップです。 ヘッドレスCMSは、データを管理するための高機能なダッシュボードと、データを提供するAPIのみを提供するアプリケーションです。 Strapiの最新バージョンでは、Reactコンポーネントで構築されたデザインシステムを提供しています。 ビルドツール 1位: Vite +21.4k 2位: esbuild +12.9k 3位: swc +8.8k 4位: Turborepo +4.5k 5位: Webpack +3.2k 2021年は、既存のトレンドが強化された年でした。 ネイティブES modulesの採用が続き、Viteは広く受け入れられました。 一方Node.jsエコシステムにおいてもVitestのようなES modulesフレームワークが作られつつありますが、こちらは一筋縄ではいきません。 TypeScriptに至ってはES modules対応を延期したほどです。 JavaScript以外の言語で作られたフロントエンドツールが増えつつありますが、これは主にパフォーマンスの理由からです。 Lee RobinsonがRust Is The Future of JavaScript Infrastructureという記事を書いています。 Rustの興味深い点は、その素晴らしいパフォーマンスと、JavaScriptとの親和性の良さです。 NAPI-RSはシリアライズ無しにJavaScriptとRust間の通信を行うことを可能とし、Next.jsはSWCにその将来を賭けました。 Parcel2はRustコンパイラをひっさげて登場しました。 Romeも全面的にRustに移行するということでしたが、創業者のひとりであるJamie Kyleは何の情報もないまま会社を離れました。 Rustは非JS言語の代表格ですが、決して唯一ではありません。 素晴らしいパフォーマンスを発揮する言語は他にもあり、BunはZigで書かれており、TurborepoやesbuildはGo製です。 そういえばEvan WallaceがFigmaを去ったのは、esbuildの開発に時間を割けるようになったからかもしれません。 monorepo分野ではLernaが最も使われていますが、既にあまり保守されていません。 次を狙って、ビルド時間を大幅に短縮できるモノレポツール Nxが急成長しています。 さらにライバルであるTurborepoが、Vercelに買収された直後から攻勢を仕掛けています。 Vueエコシステム 1位: Slidev +16.9k 2位: Vue Element Admin +9.7k 3位: Headless UI +8.6k 4位: Native UI +7.3k 5位: vue-next +6.7k Vue3が正式リリースされてから1年が経ち、多くの革新的な技術が出揃い、エコシステムが急激に成長していることがわかります。 Vue3の新しい<script setup>のような構文は、コンポーネントの開発体験を新しいレベルへと引き上げてくれます。 VS CodeエクステンションVolarは、VueにファーストクラスTypeScriptサポートをもたらし、Composition APIで一から構築されたステート管理ツールPiniaはVuexの後継者となります。 VueのデフォルトツールがViteになったことにより、Nuxt 3、Quasar、VitePressといった多くのフレームワークもデフォルトでViteを使うようになりました。 これによって開発者のエクスペリエンスが大幅に改善され、イノベーションの扉が開け放たれました。 またコミュニティは、Vue2のDXをVue3へと揃え、よりスムーズに移行プロセスが成功するように多大な努力を払いました。 2021年はVue開発者にとって、アプリのDXとパフォーマンスの両方において多くの改善を得ることのできた素晴らしい年でした。 2022年にはどんなことがおこるのか、今から楽しみですね。 Reactエコシステム 1位: Next.js +19.7k 2位: Ant Design +10.9k 3位: MUI +10.0k 4位: Remix +9.4k 5位: react-use +9.3k まもなくReact18がリリースされます。 既にRC版を触ることは可能で、Automatic Batchingによるレンダリング削減や、SSRのSuspenseサポートなど、すぐに利用できる改善がいくつもあります。 React18は待望のConcurrent Renderingが実装され、大きな破壊的変更なしにSuspenseが刷新されました。 startTransitionなど一部の機能は18.0リリース当初から使用可能になる予定です。 しかし、昨年のRising Starで紹介したServer Components等については、もう少し待つ必要がありそうです。 Reactはブラウザでもサーバでも進化を続けており、React Nativeのプラットフォーム構想も進行中で、ますますユビキタスな存在になろうとしています。 CSS in JavaScript 1位: vanilla-extact +4.4k 2位: Styled Components +3.5k 3位: Stitches +2.8k 4位: Twin +2.3k 5位: Emotion +2.0k テスト 1位: Playwright +11.9k 2位: Storybook +10.9k 3位: Cypress +9.1k 4位: Puppeteer +7.6k 5位: Jest +3.8k モバイル 1位: React Native +7.6k 2位: Ionic +3.1k 3位: Expo +3.0k 4位: Quasar +2.9k 5位: Flipper +2.5k デスクトップ 1位: Tauri +18.0k 2位: Electron +11.1k 3位: Svelte NodeGUI +2.6k 4位: NodeGUI +1.7k 5位: Neutralino +1.7k 静的サイト 1位: Next.js +19.7k 2位: Astro +8.8k 3位: Nuxt +6.2k 4位: Nuxt3 +4.0k 5位: Gatsby +3.6k 状態管理ライブラリ 1位: Zustand +6.6k 2位: XState +4.2k 3位: Jotai +4.2k 4位: Recoil +4.2k 5位: Immer +3.0k GraphQL 1位: Prisma +12.0k 2位: Hasura GraphQL Engine +5.2k 3位: Redwood +4.5k 4位: Gatsby +3.6k 5位: GraphQL Code Generator +2.0k まとめ モダンなサイト作成は、メタフレームワークの時代に突入したようです。 Next.js、Nuxt、SvelteKit、そして期待の新星Remix等が鎬を削っています。 JavaScriptコミュニティの高名なメンバーの多くは、様々なソリューションに取り組むため技術企業に参画しました。 Kent C. DoddsはRemixのチームに加入しました。 Remixを「素晴らしいユーザ体験をもたらし、コードに満足する」ことを可能にすると高く評価しています。 Vercelは多くのメンバーを雇いました。 Svelteの開発者Rich Harris、ReactコアチームのSebastian MarkbågeにJared Palmer、そしてTurborepo。 まるでドリームチームです。 ツールに目を向けると、その多くが速度を求めてJavaScriptからRustやGoといった言語にシフトしました。 Lee RobinsonはRustの台頭について、RustはJavaScriptインフラの未来だと幾度となく力説しました。 ・TauriはRust製です。 ・RomeはJavaScriptからRustへの移行を表明しています。 ・Next.jsの最新バージョン12には、Rustで書かれたswcコンパイラが同梱されています。 swcは、2020年の当ランキングで優勝したサーバサイドランタイム、Denoでも使われています。 そしてDenoは当時よりさらに進化しています。 Deno Deployが公開され、これはサーバレスにデプロイするソリューションです。 サーバレスといえば、エッジコンピューティングも2021年の重要なテーマでした。 Vercel Edge FunctionsにCloudFlare Workers、Netlify Edgeといった、ユーザのそばでバックエンドコードを実行するソリューションが多数現れました。 Next.jsやRemixといったメタフレームワークはエッジコンピューティングを活用し、バックエンドコードとReactアプリケーションの統合を容易にしてくれます。 2022年、我々はJavaScriptフルスタックアプリケーションの黄金時代に突入するのでしょうか? 感想 可及的速やかにReactが絶滅しますように。 2021年5月に登場したzxが、唐突に全てをかっさらって一躍トップに躍り出ました。 Bashで高度なプログラミングを行うことは当然に可能ですが、他のLL言語とはパラダイムがだいぶ異なるので慣れないと使いにくいです。 それなら、郷に従うより最初から慣れた言語で書いたほうが楽ですよね。 とみんなも思ったのか、フロントと同じ言語でシェルスクリプトが書けるzxが大人気になったようです。 何故かQiitaには記事が全くありませんが。 昨年の王者Denoは今年は32位であり、一気に落ち込みました。 といってもNode.jsはさらに低い40位であり、いずれも累計スター80k程度で同じくらいなので、単にその分野の人がみんなスターを付け終わったからというだけかもしれません。 フロントエンドはReact・Vueの2強、次いでSvelteとAngularでだいたい落ち着いてきた感がありますね。 一方でそれ以外の分野については、またぞろ喧しくなってきそうな気配があります。 果たして2022年はどのようなライブラリが現れてくるのでしょうか。 Houkago Atelier Toiro ha iizo
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ajaxzip3からYubinBangoに変更したが相変わらず素晴らしい件

はじめに 今まで郵便番号の自動保管にajaxzip3を利用していました。 しかし、新しいプロジェクトで導入しようと思いリポジトリを開くと以下の記載がありました。 新規の設置には yubinbangoライブラリの使用をオススメしています。 yubinbango? なんぞそれ? って感じだったので実際に触ってみましたので紹介します。 やり方 とりあえずREADMEに従い実装してみた <script src="https://yubinbango.github.io/yubinbango/yubinbango.js" charset="UTF-8"></script> <form class="h-adr"> <span class="p-country-name" style="display:none;">Japan</span> 〒<input type="text" class="p-postal-code" size="8" maxlength="8"><br> <input type="text" class="p-region p-locality p-street-address p-extended-address"/><br> </form> 郵便番号を入力すると自動的に保管してくれます! 素晴らしい!! もちろん郵便番号が全角数字で入力された場合は半角数字に自動変換します。ハイフンが入力されても動作します。 最後に 「クライアントから良く自動的に住所を入れれるようにしたい!」と要望をいただくことが多いので良くお世話になっているライブラリさんです! 興味のある方は実際に導入してみるのも良いのではないでしょうか?
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScript の Fetch API を用いた HTTPリクエストで簡略な時間的制約を設ける

タイトル通りの内容の、ちょっとしたメモです。 今回の内容 Fetch API などを使って HTTPリクエストを実行する際に、例えば「1度 HTTPリクエストを送ったらその後 5秒間は HTTPリクエストを送る処理を実行しないようにする」というような、時間的制約をかける話です。 指定の時間が経過するまでは HTTPリクエストを実行しようとしてもその処理をブロックするようなフラグを立てておく、という形で実現してみます。 時間的な制約をかける 「処理をブロックするようなフラグ」という話を書きましたが、このフラグがオンになった後に、一定の時間が経過したらフラグがオフになる、というような処理を実装します。 具体的には、「JavaScript で sleep処理を実現する方法」というような方向性で情報を探した時によくでてくる、「async/await を使った sleep処理」を使います。 ●非同期関数 - JavaScript | MDN  https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Statements/async_function ●awaitできるsetTimeoutを1行で書く方法 - Qiita  https://qiita.com/suin/items/99aa8641d06b5f819656 ちなみに sleep処理を実現する部分のメインの処理は、以下のような形になります。 await new Promise(resolve => setTimeout(resolve, 3000)); Fetch API による HTTPリクエスト まずは、Fetch API でシンプルに HTTPリクエストを実行する処理について、HTTPBin(httpbin.org) を利用したものを準備します。 なお、HTTPBin を使って実行できる処理はいくつか種類がありますが、今回は https://httpbin.org/json へのアクセスによる JSON の取得を行ってみます。 <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <title>Fetch API による HTTPリクエスト</title> </head> <body> <h1>Fetch API による HTTPリクエスト</h1> <button id="button01">HTTPリクエストを実行</button> <script> let waitFlag = false; const url = "https://httpbin.org/json"; const button01 = document.getElementById("button01"); button01.addEventListener("click", httpGet, false); async function httpGet() { const res = await fetch(url); let json; try { if (res.ok) { json = await res.json(); } else { throw new Error(res.status); } } catch (e) { console.error(e); } console.log(json); } </script> </body> </html> 上記をブラウザで開いてページ上のボタンを押下すると、HTTPリクエストで取得できた JSON の情報が、ブラウザのコンソールに出力されるのが確認できるかと思います。 時間的な制約をかけた HTTPリクエスト 次に、上記の HTTPリクエストを行ったものに、以下の処理を利用して時間的な制約をつけます。 (async () => { console.log("待ち開始"); await new Promise(resolve => setTimeout(resolve, 5000)); console.log("待ち終了"); waitFlag = false; })(); 以下に、処理を行うためのソース全体を掲載します。 <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <title>HTTPリクエストの連続実行に時間的な制約をかける</title> </head> <body> <h1>HTTPリクエストの連続実行に時間的な制約をかける</h1> <button id="button01">HTTPリクエストを実行</button> <script> let waitFlag = false; const url = "https://httpbin.org/json"; const button01 = document.getElementById("button01"); button01.addEventListener("click", httpGet, false); async function httpGet() { if (!waitFlag) { waitFlag = true; const res = await fetch(url); let json; try { if (res.ok) { json = await res.json(); } else { throw new Error(res.status); } } catch (e) { console.error(e); } console.log(json); (async () => { console.log("待ち開始"); await new Promise(resolve => setTimeout(resolve, 5000)); console.log("待ち終了"); waitFlag = false; })(); } } </script> </body> </html> 上記をブラウザで開いてページ上のボタンを押下すると、1回目は先ほどの時間的な制約がないものと同様の動きをするのが確認できるかと思います。 さらに、1回目のボタン押下の直後から 5秒経過するまでの間は、再度ボタンを押下しても HTTPリクエストが実行されない(取得した JSON の出力が行われない)のも確認できるかと思います。 【追記】 同時に処理できる数で制限する この記事を公開した後、「非同期処理を同時に実行できる数を制限する、みたいな話を最近見た気がする」と思って、かつ、それを見返したくなることがあるかも、と思ったのでここへ追記する形でメモ。 ●Javascriptで同時に非同期処理を行う | DevelopersIO  https://dev.classmethod.jp/articles/resolve-promise-at-same-time/
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む