Webを支える技術③~HTTP~
前回はWebを支える3つの技術のうち、URIについてまとめた。
- URI
- HTTP
- HTML
概要(主にRESTについて)はこちら
Webを支える技術①~RESTとは~ - すなのプログラミングノート
URIについてはこちら
Webを支える技術②~URI~ - すなのプログラミングノート
今回はHTTPについて。
正直Webについての勉強と言えばこのHTTPくらいしかイメージがなかった。
それくらい存在感はあるけど、体系的に学ぶいい機会になったのでここでちゃんとまとめておく。
TCP/ IPとは
いきなりHTTPじゃないのかよって感じだが、HTTPはTCP/ IPというネットワークのプロトコルをベースにしている。
つまり、HTTPについて知る上で、TCP/ IPの基礎知識について知っておく必要があるというわけ。
まず、インターネットのネットワークプロトコルは階層型になっている。
階層型にする利点は、下の層の内容に左右されることなく上の層を実装できるということ。
階層型プロトコルは以下の通り。
階層名 | 役割 | 具体例 |
---|---|---|
アプリケーション層 | アプリケーションごとのやりとりを規定 | HTTP, NTP, SSH, SMTP, DNS |
トランスポート層 | データの分割や品質保証を規定 | UDP, TCP |
インターネット層 | ネットワーク間の通信を規定 | IP |
ネットワークインターフェース層 | ハードウェアに関する規定 | イーサネット |
ネットワークインターフェース層
一番下の層であり、物理的なケーブルやネットワークアダプタに相当する部分
インターネット層
ネットワークでデータを実際にやり取りする部分。
TCP/ IPではIPが相当する。
IPではデータの基本的な通信単位をパケットと呼ぶ。
IPでは自分のネットワークインターフェースでデータを送り出すことだけを保証している。
送り出したデータがどこへ行こうと知ったこっちゃない、後は任せたという大雑把なやつ。
トランスポート層
IPが保証しなかったデータの転送を保証する。
インターネット層の尻拭き的な役割。
TCP/ IPではTCPが相当する。
接続先の相手に対してコネクションを張って、データの抜け漏れをチェックする。
転送するデータがどのアプリケーションに渡るのかを決定するのはポート番号。
TCPによるコネクションの確立は3ウェイハンドシェイクと呼ばれる。
- クライアントからの接続要求(SYN)
- クライアントに対する確認応答(ACK)&サーバーからの接続要求(SYN)
- サーバーに対する確認応答(ACK)
アプリケーション層
具体的なインターネットアプリケーションを実現する層で、HTTPやDNS, メールなどが相当する。
TCPでプログラムを作る時はソケットと呼ばれるライブラリを使うのが一般的。
ソケットはネットワークでのやり取りを抽象化したAPIで、接続や送信、受診などの基本的な機能を備えている。
HTTPサーバやブラウザはソケットを用いて実装する。
これらが階層化されていてHTTPはその最上階にいる。
そのため、データの転送保証などは下位層に任せて、具体的なデータのやり取りの規定のみに専念できる。
HTTPのバージョン
HTTPはHTTP/0.9, HTTP/1.0, HTTP/1.1, HTTP/2のように機能追加と改良がされてきた。
HTTPキープアライブ
HTTP/1.1で実装された機能。
これはどういうものかというと、コネクションを継続して利用できる方式。
通常webページ内に画像が埋め込まれていた場合、
コネクションの確立→webページを受け取った後にコネクションを閉じる→画像を取得するためコネクション確立
と、HTTPリクエストごとにコネクションを確立しなければならなかった。
これが一連の流れを全てコネクションつなぎっぱなしでできるので無駄な通信を省くことができるようになった。
HTTPパイプライン
これもHTTP/1.1で実装された。
それまではHTTPレスポンスを受け取るまでは次のHTTPリクエストを送れなかったが、HTTPパイプラインではHTTPレスポンスを待つことなく複数のHTTPリクエストを送信することができる。
ただし、レスポンスの処理はリクエストを行った順番にしなければならないため、待ち状態が発生することもある。
ストリーム機能
HTTP/2で実装された機能。
これは1つのコネクション上に仮装の通信経路「ストリーム」を生成するというもの。
それぞれのストリームでは独立しHTTPリクエスト、レスポンスができる。
先ほどのHTTPパイプラインとは異なり、ストリームによる多重化ではレスポンスを受ける順番も自由なので、待ち状態が発生しない。
サーバープッシュ
これもHTTP/2で実装された機能。
それまではファイル内に画像が埋め込まれていた場合、
①ファイルのリクエスト、レスポンス、読み込み
②画像のリクエスト、レスポンス、読み込み
と、2回に分けてHTTP通信を行う必要があった。
サーバープッシュではあらかじめサーバー側で必要なファイルを判別して、HTTPリクエストなしに、事前にwebサーバーから転送することができる。
HTTPメソッド
HTTPメソッドクライアントからサーバへ様々な情報を伝えるにも関わらず、実はたったの8つしかない。
GET, POST, PUT, DELETE, HEAD, OPTIONS, TRACE, CONNECT
TRACE, CONNECTはあまり使われない。
POST
POSTはリソースを作成するのが最も代表的な機能だが、それだけではなく既存リソースへのデータの追加もできる。
リソースの作成ではレスポンスは201 Createdだがデータの追加では200 OKになる。
また、PUTやDELETEはPOSTで代用することもできる。
そもそもHTMLのフォームで指定できるメソッドはGETとPOSTのみ。
この制限からGETとPOSTが最もよく利用される。
ただしAjaxの発展とともに任意のメソッドを発行できるようになった。
PUT
PUTはリソースの更新だが、リソースがない場合はリソースの作成もできる。
この時、作成するリソースのURIはクライアント側が指定することになる。
POSTの場合は、新しく作成したリソースのURIがLocationヘッダで返ってくるが、
PUTの場合はクライアントは既にリソースのURIを知っているのでLocationヘッダで返ってこない。
このようにURIの決定権がPOSTの場合はサーバに、PUTの場合はクライアントにある。
そのため、PUTの場合はサーバの内部実装を熟知した上でクライアントで指定できるURIの制限をかけなければならず、サーバとの結合が密になる。
そのため通常はPOSTを使う。
HEAD
リソースのヘッダ(メタデータ)を取得する。
OPTIONS
そのリソースがサポートしているメソッドの一覧(GET, POST, HEADなど)を返す。
OPTIONS自体はAllowヘッダに含めない。
べき等性と安全性
メソッドはべき等性と安全性で分類することもできる。
べき等性
ある操作を何度行っても結果が同じこと
安全性
操作対象のリソースの状態を変化させないこと
メソッドを分類すると以下の通り。
メソッド名 | べき等性 | 安全性 |
---|---|---|
GET, HEAD | ○ | ○ |
PUT, DELETE | ○ | × |
POST | × | × |
これらが重要なのは通信エラーが発生した時にリクエストをどう回復させるかに関わるから。
たとえばPOSTはべき等でも安全でもないため、通信エラーの後にもう一度POSTすると二重にリソースが作成される可能性がある。
ブラウザによって「もう一度送信しますか?」とダイアログが出るのはPOSTを再送しようとしている場合。
ステータスコード
- 1xx: 処理中
- 2xx: 成功
- 3xx: リダイレクト
- 4xx: クライアントエラー
- 5xx: サーバエラー
よく使われるステータスコード9選
- 200 OK
- 201 Created
- 301 Moved Permanently
- 303 See Other
- 400 Bad Request
- 401 Unauthorized
- 404 Not Found
- 500 Internal Server Error
- 503 Service Unavailable
紹介されていたのはこの9つだが、403 Forbiddenもよく見る気がする。
ページ自体は存在するものの、閲覧権限がなくて見れないってやつ。
ただ自分で実装する時はよく見るってだけで、通常のアプリでは403の状態でも404 Not Foundでそもそもページ自体存在しないよってレスポンスするように実装している気がするので、通常のアプリで見ることはあまりないのかもしれない。
303がちょっとわかりにくかった。
これはリクエストを処理した後、別のURIへ誘導するステータスコード。
たとえばフォーム送信後、完了ページへは GET でリダイレクトするようなケースが相当する。
503はサーバメンテナンスなどで一時的にアクセスできない場合。
ボディにはその理由が入る。
HTMLについて
Webを支える技術の3つ目はHTML。
他のハイパーメディアだと、AtomとかJSONとか。
HTMLやJSONについてはあまりまとめるほどのものでもないなって思ったのと、他のAtomとかそういうのはよくわからんて感じだったのでまとめは省略。
おわりに
本についてまとめてみたけど、まとめ作業を通してわりと概要はつかめたし重要そうな知識は定着したのではないかと思う。
大変だったけど、エンジニアとして必要な基礎知識と思うのでまとめてよかったかな。
最近はこういう根幹部分の技術に興味あり。
今は「マスタリングTCP/ IP 入門編」を読んでる。
参考
Webを支える技術(書籍) https://amzn.to/2AYsEy7
Web技術の基本(書籍) https://amzn.to/2YhQsFX
Webを支える技術②~URI~
前回はWebを支える技術の概論として主にRESTについてまとめた。
Webを支える技術①~RESTとは~ - すなのプログラミングノート
今回はその続き、Webを支える技術の3つのうちのURIについて。
- URI
- HTTP
- HTML
URIとはUniform Resource Identifierの略で、「リソースを統一的に識別するID」のこと。
つまり、「同じルールのもと制定された、リソースを一意に指し示すための名前/ ID」のこと。
そのためURIさえあれば全てのリソースに対して簡単にアクセスできる。
ちなみにURLはUniform Resource Locationの略なので、URIとは異なるがまぁ同じと思って差し支えない。
URIの方が広義の意味になる。
URIの構文
URIの構文はよく見るこんな感じのやつ。
http://blog.example.jp/entries/1
これをパーツに分解すると以下のようになる。
- URIスキーム: http
- ホスト名: blog.example.jp
- パス: /entries/1
ホスト名はDNSで名前解決できるドメイン名かIPアドレスで、インターネット上で必ず一意となる。
ちなみにスキームの後の「:」はスキームとその後ろの区切りを示し、「//」はユーザー情報とホスト名の開始を知らせる文字列。
これはよく見る形だが、実はもっと複雑な情報を持たせることもできる。
それがこちら。
http://suna:pass@blog.example.jp:8000/search?q=test&debug=true#n10
めっちゃ複雑になった。。。
さっきの例ではなかったものを挙げると以下のようになる。
- ユーザー情報: suna:pass
- ポート番号: 8000
- クエリパラメータ: q=test&debug=true
- URIフラグメント: #n10
ユーザー情報はリソースにアクセスする際のユーザー情報で、ユーザー名とパスワードを「:」で区切る。
@の後ろにホスト名が続く。
ポート番号は省略するとデフォルトで、HTTPのデフォルトは80。
クエリパラメータは例えば検索キーワードなど、クライアントから動的にURIを生成する時に利用する。
絶対URIと相対URI
これはディレクトリにおける絶対パス と相対パスと同じ。
相対URIの場合、起点となるURIを設定する必要があり、これをベースURIと呼ぶ。
通常このURIはHTMLなどの中で明示的にベースURIを指定する必要がある。
HTMLの場合は<head>の中に<base>を入れる。
<head> <title>test web page </title> <base href="http://example.jp/" /> ... </head>
URIで使用できる文字
URIの使用で許可されてる文字以外をURIに入れる場合には%エンコーディングでその文字をエンコードする。
「http://ja.wikipedia.org/wiki/あ」のような日本語の物は%エンコードされるため、こんなようなURIになる。
http://ja.wikipedia.org/wiki/%E3%81%82
確かにAmazonのリンクとかよくこんな風になってるの見てたけどそういうことかと納得。
マトリクスURI
サーバ側でURIを設計する際は様々な注意点があるが、あまり自分で使うことがイメージできなかったのでざっくり省略。
ここではマトリクスURIについてのみまとめておく。
URIを設計する際にパスを使うことが多いが、必ずしも階層構造で表現できないものがある。
たとえば地図サービスでは緯度や経度などの他に表示スケールや航空写真かどうかのフラグなど複数のパラメータを組み合わせる必要がある。
このように複数パラメータの組み合わせで表現するリソースにはマトリクスURIを使う。
階層構造の表現は「/」だったが、マトリクスURIでは複数パラメータをそれぞれ「;」で区切る。
以下は一例。
http://example.jp/map/lat=35.705471; lng=139.751898
latが緯度、lngが経度。
現在は一般的にマトリクスURIの表現には「;」か「,」を使う。
使い分けは以下の通り。
「;」: パラメータの順序が意味を持たない場合 「,」: パラメータの順序が意味を持つ場合
URIの不透明性
クライアント側のURIの性質について。
URIは可読性が高いため、ユーザーがURIの構造を推測しやすくなる。
URIの内部構造を推測して操作したりクライアント側でURIを構築したりすべきではない。
なぜなら推測してもそこにリソースがあるとは限らないし、サーバ側の実装でURIの構造を変更した場合に、システムが動かなくなるという密結合状態になるから。
したがって、URIをクライアント側で組み立てたり、拡張子からリソースの内容を推測したりできないことを、「URIはクライアントにとって不透明である」と言う。
クライアントを作る際は、URIを不透明にすべき。
URIに関してはアプリケーションのフレームワーク がやってくれちゃうので、普段意識することはないけどいい勉強になった。
次回はHTTPについて。
Webを支える技術③~HTTP~ - すなのプログラミングノート
参考
Webを支える技術(書籍) https://amzn.to/2AYsEy7
Web技術の基本(書籍) https://amzn.to/2YhQsFX
Webを支える技術①~RESTとは~
よくオススメされてる「Webを支える技術」を最近読んだのでまとめる。
その後、「Web技術の基本」という本もオススメされて読んだがこちらの方が基礎的でわかりやすかった。
Webを支える技術
→Web技術の基本
→Webを支える技術(2回目)
という順序で読んだが、Web技術の基本から読むことを強くオススメする。
いきなり結論、Webを支える技術は以下の3つ
- HTTP
- URI
- HTML
全てを一つの記事でまとめると膨大な量になるので、まずはこの記事では概論をまとめることにする。
それだけでも6000字の中々のボリュームになってしまったが。。。
別記事でHTTPについてとURIについてをまとめる。
HTMLはまとめるほどのものでもないなって思ったのと、他のAtomとかなんやかんやはよくわからんて感じだったのでとりあえずは放置の方向で。
つまり本記事を入れて全部で3部作になる。
URIについてはこちら
Webを支える技術②~URI~ - すなのプログラミングノート
HTTPについてはこちら
Webを支える技術③~HTTP~ - すなのプログラミングノート
今回は概論だが主にRESTについての説明。
情報システムとしてのweb
webを情報システムとして見ると以下の2つの側面を持っている。
- ハイパーメディアシステム
- 分散システム
ハイパーメディアシステム
ハイパーメディアとはテキストや画像などの様々なメディアをハイパーリンクで結びつけたシステム。
ざっくり言ってしまうと、いろんな情報がハイパーリンクで繋がってるシステム。
webとweb以前のハイパーメディアとの一番の違いはインターネットを使っているかどうか。
webはインターネットを用いることで様々な情報をリンクさせることができ、システムを大規模化しやすくなっている。
webはクモの巣を意味する。
まさにクモの巣のようにいろんな情報が結びついている。
分散システム
分散システムは複数のコンピュータをネットワーク上に分散して配置し、効率的に処理するようにしたシステム。
対義語は集中システムで、1つのコンピュータが様々なあらゆる処理を行うというもの。
webは世界中に配置されたサーバーに世界中のブラウザがアクセスする分散システム。
それまでの分散システムは広まらなかった一方で、webがここまで広まったのはプロトコルがシンプルだったから。
それまでの分散システムでは負荷分散の問題など様々な問題があったが、webではHTTPというシンプルなプロトコルを使うことにより、これらの問題を解決することができた。
シンプルイズベスト。
後々のキーワードにもなるが、あらゆる"制約"によって必要最低限のシンプルさを保つというのは標準化を行う上で重要であり、それによって広く普及する技術となりうる。
根幹となる技術はシンプルであるべきなんだなと思った。
シンプルであればその上でいかようにも派生できる。
目玉焼きはシンプルだからこそ塩、醤油、ソースなど色んな味付けができるけど、もしあれが最初から味噌を練り込むメニューですとかだったら普及しないだろうな。。
ちなみに目玉焼きは醤油派。
RESTとは
RESTはRepresentational State Transferの略。
webの急速な普及に伴い、各社がそれぞれ勝手に独自のスパイスを混ぜていくと相互運用できなくなる。
そのため、あらかじめある程度のガイドラインを定めてそれに沿ってwebを設計する必要がでてきた。
このweb標準化のために様々な設計思想が提案されたが、その中で現在最も普及したのがRESTというわけである。
それぞれが勝手なことするとまとまりなくなるから、機能性は担保しつつ簡潔なルールを設けましょうという話。
つまりRESTはwebの設計思想でありアーキテクチャスタイルである。
アーキテクチャスタイルとはアーキテクチャより抽象度が一つ高いもの。
アーキテクチャにはブラウザ、サーバ、HTTP、HTMLなどがある。
これらを組み合わせてwebを設計しましょうってのが、アーキテクチャスタイルであるRESTがざっくり意味するところ。
抽象度 | webでの例 |
---|---|
アーキテクチャスタイル | REST |
アーキテクチャ | ブラウザ、サーバ、HTTP、HTML |
実装 | Apache、Google Chrome |
ちなみにRESTはネットワークシステムのアーキテクチャスタイルであって
それ以外の例だとMVC(model-view-controller)とかもアーキテクチャスタイルのレイヤーになるらしい。
同じネットワークシステムの別のアーキテクチャスタイルだとP2Pなど。
ブロックチェーンで使われるやつ。
詳細は後述するが、RESTというのはクライアント/ サーバのアーキテクチャスタイルにいくつかの"制約"を加えたものである。
制約を加えることで、組み合わされたそれぞれのコンポーネントが好き勝手動作することなく、全体で強調して動作するようにできる。
リソースとは
RESTについての説明に入る前に、リソースの説明をしておく。
リソースはRESTにおける重要な概念の一つ。
一言で言うと、「web上に存在する、名前を持ったありとあらゆる情報」である。
それぞれのリソースは一意のURIを持ち、このURIを用いることでプログラムはリソースが表現する情報にアクセスできる。
このURIが持つ、リソースを簡単に指し示せる性質のことをアドレス可能性と呼ぶ。
つまり、名前がちゃんと付いてて適切な手段でアクセスできる状態であること。
ちなみにURIとURLは厳密には違うが、URIをURLに読み替えても支障はない。
リソースとは「web上に存在する情報」という抽象的な概念であり、具体的にクライアントとサーバ間でやりとりするデータのことをリソースの表現と呼ぶ。
なんやかんや書いたけど、リソースってのは情報のことなんだなくらいわかってればいい気もする。
RESTの構成
先述したようにRESTとはクライアント/ サーバにいくつかの制約を課して構成されている。
そのために、いくつかのアーキテクチャスタイルが組み合わされて構成されている。
結論、RESTは次の6つを組み合わせたアーキテクチャスタイルである。
- クライアント/ サーバ
- ステートレスサーバ
- キャッシュ
- 統一インターフェース
- 階層化システム
- コードオンデマンド
図にするとこんな感じになる。
以下で一つずつ詳細に記述する。
クライアント/ サーバは省略。
ステートレスサーバ
ステートレス: クライアントのアプリケーション状態をサーバ側で管理しないこと。
ステートフル: クライアントのアプリケーション状態をサーバ側で管理すること。
つまりステートフルだと、同じクライアントから2回目以降アクセスがあった時に前の状態を記憶しているということ。
以下はステートレスの場合のクライアント/ サーバの例。
ファストフード店での注文で
客(クライアント): ポテトください
店員(サーバ): はい、サイズは?
客(クライアント): Mで
店員(前回のやり取り覚えてないサーバ): 何のこと?
えぇ。。。(困惑)
こう見るとステートフルにすべきって感じもするが、これのデメリットはサーバの数が増えた時に、クライアントの状態管理を全てのサーバ間で同期しないといけなくなる。
あるいは同一クライアントからのアクセスは必ず同一サーバに振り分けるような仕組みを導入しなければならない。
要するにサーバ側の負荷が高くなる。
ステートレスであれば、クライアントの情報は持たなくていいのでサーバ側の実装を簡略化できる。
しかし、UX的にはステートフルの方がいいことも多々あり、HTTPをステートフルにする代表的な方法はCookieを使ったセッション管理。
これは厳密にはRESTの視点から見ると間違っているが、UXを考えると使わざるを得ない場合もある。
キャッシュ
取得したリソースの鮮度に基づき、それをクライアント側で使い回す方式。
これにより、クライアントとサーバ間の通信量を減らすことができる。
統一インターフェース
リソースに対して統一された限定的なインターフェースで操作を行うアーキテクチャスタイルのこと。
たとえばHTTPではGET, POST, PUT, DELETE, HEAD, OPTIONS, TRACE, CONNECTの8つのメソッドのみが使える。
たったこれだけに限定されているため、様々なアーキテクチャ間のやりとりが統一され、全体のアーキテクチャがシンプルになる。
また、インターフェースが統一されることで、クライアントやサーバの実装の独立性が向上する。
webが多様なクライアントやサーバで実装されているのは、統一インターフェースが重要な役割を占めており、RESTを最も特徴付けるアーキテクチャスタイル。
階層化システム
システムを階層化し、しかもそれが統一インターフェースで接続できることで、クライアントはサーバもプロキシも同じインターフェースで接続できる。
つまり、クライアントは接続先が変わったことを意識しなくて良くなる。
コードオンデマンド
プログラムをサーバからダウンロードし、クライアント側で実行するアーキテクチャスタイル。
クライアントを後から拡張できるが、アプリケーションプロトコルの可視性が低下する。
HTTPの間は通信の意味が明確だったが、クライアント側でプログラムを実行すると通信内容が不透明になる。
RESTfulなWebサービス
ここまででRESTの構成を見てきたが、RESTはあくまでアーキテクチャスタイルなのでこれを基にして色々と調整してよい。
RESTの規約に基づいていてRESTらしいことをRESTfulという。
なんか結構定義曖昧だな。。
別の書籍ではRESTfulなWebサービスの性質として、アドレス可能性、ステートレス性、持続性、統一インターフェースの4つを挙げている。
持続性以外は本文中で述べた。
持続性とはリソースをリンクで接続して1つのアプリケーションをなすという性質。
要はハイパーリンクでつながってるよってことかな。
重要性としては
アドレス可能性 > 持続性 > 統一インターフェース > ステートレス性
おわりに
次回以降の記事でそれぞれの技術を詳しく見ていく。
URIについてはこちら
Webを支える技術②~URI~ - すなのプログラミングノート
HTTPについてはこちら
Webを支える技術③~HTTP~ - すなのプログラミングノート
参考
Webを支える技術(書籍) https://amzn.to/2AYsEy7
Web技術の基本(書籍) https://amzn.to/2YhQsFX
async, awaitでの非同期処理の書き方
asyncやらawaitやら
非同期処理をする時に使うんだろうなーというくらいの認識でいたけどちゃんと分かってなかったのでまとめる。
そもそも同期処理と非同期処理とは
同期処理
リクエストした後、レスポンスが返ってくるまで待ってる処理
非同期処理
リクエストした後、レスポンスが返ってくるのを待たずにその後の処理を継続する。
レスポンスが返ってきた時にその処理の続きを行う。
定義としては違うんだろうけど、特徴としてはこんな感じ。
処理待ちの間に別の処理を行いたい時には非同期処理にするとよい。
ご飯炊けるの待ってる間に味噌汁作る的な感じ。
非同期処理の中にも逐次処理と並列処理がある
ここがちゃんと分かってなかった。
逐次処理は分かりやすい。
下記のような感じでメソッドチェーンを使って処理1→処理2→処理3と順番に処理を行う。
これって同期処理じゃないのって感じだったけど、あくまで非同期処理の中で同期的に処理を行うという認識でいる。
myPromise(処理1).then(処理2).then(処理3)
並列処理はいくつかの非同期的な処理を並行して行う。
つまり複数の処理を同時並行で行って、それが全部終わった段階で初めて次の処理を行うというもの。
たとえば目玉焼きカレーを作る時に
- ご飯を炊く
- カレーを作る
という操作を同時に行って両方終わった後で皿に盛り付けて、最後に目玉焼きを乗せる的なイメージ。
同期処理と非同期処理のまとめはこんな感じ。
そしてここからが本題。業務で非同期処理を行ったがうまくいかなかった。
結論、並列処理とmapメソッド内での非同期処理がうまく書けてなかったので書き残しておく。
まずは基本的な逐次処理から
非同期処理はpromise使って.thenとか.catchとかやるんだけどそれをasyncとawaitを使ってもできる。
この辺よく分からないまま、
業務でasyncとかawaitとかを使う
→promiseについてちゃんと学ぶ
という完全に順番逆ーな感じで勉強した。
今となっては全体的な概要が理解できたつもりだが、結局asyncとawaitの方が記述が簡潔だし分かりやすいと感じたので、今回もasyncとawaitで記述する
async func1 = () => { await 処理1 await 処理2 await 処理3 return ... };
thenでつなげると長くなって見にくくなるけど、awaitだと1行で済むからとても見やすい
これで処理2は処理1が終わってから行われるし、処理3も然り
処理3が終わって初めてreturnされる
並列処理
処理1と処理2を並列で行って、どっちも終わってから処理3を行いたいという場合は、Promise.allを使うとよい
書き方はこんな感じ
async func1 = () => { await Promise.all([処理1, 処理2]) await 処理3 return ... }
Promise.allの後に並列で処理したいものを配列?として[ ]の中に記載する。
[ ]の中の処理が全て終わった段階で次の処理に行くことになる。
ちなみにPromise.raceを使うと、処理1、処理2のうちどちらかが終わった段階で次の処理に行く。まさにrace。
mapメソッドの中での非同期処理の使い方
業務でハマったのはここ。
配列があって、その配列の中身をmapメソッドを使って一つずつ取り出して、そのたびにAPIを叩く処理を実装しようとした。
async func1 = () => { await array1.map(item => API呼ぶ処理(item) ) await array2.map(item => API呼ぶ処理(item) ) 処理3 }
array1の全ての要素に対してAPI叩いた後、array2の全ての要素でAPIを叩くという操作をしたかったが、うまくいかない。
というのもmapメソッドの中身まではawaitしてくれないらしい。
じゃあmapメソッドの中でawaitさせてあげればいいんだなと思ってこうした。
async func1 = () => { await array1.map(item => await API呼ぶ処理(item) ) await array2.map(item => await API呼ぶ処理(item) ) 処理3 }
すると、asyncないのにawait使ってんじゃねーよって怒られた。
async宣言する必要があるけどどこに宣言すればいいんだ?と思ったけど、
以下のようにすればいいらしい。
async func1 = () => { await array1.map(async item => await API呼ぶ処理(item) ) await array2.map(async item => await API呼ぶ処理(item) ) 処理3 }
最初は「え、そこに入れるの!?」って思ったけど、よくよく考えたらasyncは関数の前に宣言するし、mapメソッドの中身はコールバック関数だし何も不思議じゃない。
当たり前のことだけど、ここでハマったおかげで非常に勉強になった。
ちなみに先ほどの2つの処理で使うAPIは同じもの、かつarray1とarray2の順不同だったので並列処理を使って以下のようにまとめた。
async func1 = () => { await Promise.all( [array1, array2].map(async item => await API呼ぶ処理(item) ) ) 処理3 }
これでAPIを叩く2つの処理を同時に走らせて、それらが全て終わったら処理3を行うという実装ができた。
非同期処理ちゃんと分かってなかったけど今回の件で色々調べられて非常に理解深まった。
そいえば、非同期処理って主にAPI叩く時に使ってるイメージだけど、それ以外で使うことってそんなにあるんだろうか?
参考
リンクはどれも分かりやすく非常に勉強になった。
JSの二重否定
JSのif文で二重否定が出てきた。
二重否定なんだから元に戻って意味なくね?って思ったけど、もっと便利なものだった。
結論、オブジェクトの有無をboolean型にすることができる。
状況
vueファイルの中でこんな感じにいらっしゃった。
:class = "{'hoge': !!fuga}"
これの意味するところはfugaの真偽に応じてhogeというclassを適用するかどうかということ。
つまり、動的にスタイリングできるようになる。
これ下記のような感じだったら何も不思議じゃないんだよな
:class = "{'hoge': !isFuga}"
つまりisFugaがfalseだったらhogeが適用される。
単純に否定形にしてるだけだから。
でもこれが二重否定になったら否定して否定するから元どおり。
そもそも"!!"って付けなくてよくね?ってなる。
オブジェクトをboolean型にできる
もちろん必要だからこのような記述があったわけだ。
それが、オブジェクトの有無をboolean型にできるというもの。
見た方が早い。
const obj = {name: "suna"} console.log(obj) //objの中身 console.log(!obj) //false console.log(!!obj) //true
オブジェクトを否定するとfalseになるので、もう一度否定するとtrueにできるというわけだ。
これでただのオブジェクトがboolean型に変換された。
中々うまくできている。
これが活躍するのはundefinedの時。
//objは定義されてない console.log(obj) //undefined console.log(!obj) //true console.log(!!obj) //false
定義されていない場合、そのままだとundefinedになるので、falseとは異なる。
undefinedを否定するとtrueになるので、もう一度否定するとfalseになる。
そう、undefinedを二重否定するとundefinedではなくfalseになるのだ。
これでundefinedをfalseとして使えるようになった。
中々うまくできている。(2回目)
そもそもundefinedはfalseとして処理される?
undefinedの二重否定がfalseになって条件判定に使えるー!とは思ったものの、実際にundefinedのまま以下のようにすると普通にfalseとして扱われてた。
// fugaはundefined if (fuga) { console.log("fugaあるよ!"); } else { console.log("fugaないよ!"); // 出力される }
え、じゃあ結局undefinedのままでもいいんだったらやっぱり二重否定しなくてもいいの?って感じ
おそらくだけど、これだと二重等号(==)で比較してるからあんまりよろしくないんだろうなと思っている。
つまり以下と等価なんだろうなと。
if (fuga == true) { console.log("fugaあるよ!"); } else { console.log("fugaないよ!"); }
JSではできるだけ三重等号(===)を使うべきなので厳密さに欠ける。
その点二重否定した後だとちゃんとboolean型になるから三重等号で評価できるようになる。
あと、これは想像で確かめてはいないんだけど、単純なif文じゃなくてReactとかVueのインラインで条件分岐する時に重要なのかもしれない。
最初に出てきたこの文。
:class = "{'hoge': !!fuga}"
この「!!fuga」の部分て真偽値が入らなきゃいけないからオブジェクトそのまま突っ込んでも評価されないのではないかと思っている。
boolean型のtrue or falseが入らなきゃいけないんじゃないかなと。
確かめてはいないから、違うかもしれない。
まぁオブジェクトの有無をboolean型で表現できるようになったということが分かったので今回はよしとする。
ただしデメリットも。
オブジェクトは定義さえされていれば中身が空でもtrueになるので注意。
あと数字の1はtrueだが0はfalseなど。
何がtrueで何がfalseなのかはちゃんと把握した上で使わないと逆の条件になってしまいそうだから注意。