t_hazawaの日記

株式投資とWebエンジニアリングのブログです。株式投資の目次は→です。 https://t-hazawa.hatenablog.com/entry/2021/02/12/220933

JavaScript中級者になろう 13章を読んだ (通信)

前: JavaScript中級者になろう 12章を読んだ (HTML5) - t_hazawaの日記

経緯.

  • JS力をつけてる
    • JS知識?

十三章第一回 XMLHTTPRequest

https://uhyohyo.net/javascript/13_1.html

  • 早速13000文字ある
  • Ajaxという言葉はもはや死語かもしれませんが

  • ありとあらゆるところで使われてる、ということかな?
  • Ajaxとは、AsynchronousとJavaScriptに、XMLを組み合わせて作られた造語です。

  • JavaScriptからHTTPリクエストを発行するには、XMLHttpRequestインスタンスを作ります。

  • そして、openメソッドでリクエストを開始します。このとき、パスとメソッドを渡してやります。 connectとtraceはセキュリティの観点から使えないことになっています。

  • ちなみに今回は省略していますが、第3引数は非同期フラグ(後述)で、また必要な場合、第4引数にユーザー名、第5引数にパスワードを渡すことが可能です。

  • 実はopen関数を呼ぶだけだとリクエストは送信されません。リクエストを実際に送信するのがsendメソッドです。。。sendには引数が一つあり、リクエストの本文です。

  • xhrにおいてもイベントを用います。ここで、リクエストが完了して結果が戻ってきたときのイベントはloadです。

  • onloadプロパティを設定するか、addEventListenerでイベントハンドラを登録します。

  • xhr.addEventListener("load",function(ev){ });

  • この時点で、リクエストの結果はxhrオブジェクトのresponseプロパティに入っています。

  • xhr.addEventListener("load",function(ev){ //結果を表示 document.getElementById('result').textContent = xhr.response; });

  • xhrは外で宣言してるから、このイベントリスナーの中でも使えるんだなあ
  • 結果をどんな風に受け取りたいかはXHRオブジェクトのresponseTypeプロパティに文字列を代入してやることで決定します

  • json。結果はjsonで送られてきて、それをパースした結果のオブジェクトを受け取ります。

  • arraybuffer 結果をArrayBufferで受け取ります。この方法ならばバイナリデータも受け取れます。

  • blob 結果をBlobで受け取ります。arraybufferとの違いは、Blobはtypeプロパティ(そのデータのMIMEタイプ)を持っておりこれが使用できるという点です。

  • document HTMLまたはXMLのファイルを受け取ります。結果はパースされてHTMLDocument(送られてきたHTMLファイルに対応するdocument)またはXMLDocument(XML文書の場合)で受け取ります。HTMLになるかXMLになるかは、サーバーから送られてきたデータ(のMIMEタイプ)によって自動的に判定されます。HTMLかXMLかに関わらず、この場合はresponseXMLプロパティでも結果を受け取れます。

  • 詳しくなれた
  • 上の説明でresponseTextとresponseXMLというプロパティが何気なく出現しましたが、これは昔のXHRの名残です。responseプロパティがあれば全て受け取れるので使うことはあまりないでしょうが、昔に書かれたコードに出現することがあります。

  • リクエストヘッダを設定するには、setRequestHeaderメソッドを使います。ただし、タイミングはopenメソッドのあと、sendする前でなければいけません。 xhr.setRequestHeader("Accept-Language","ja;q=0.8, en;q=0.6");

  • ただし、XHRで利用できるヘッダは制限されており、完全に自由にヘッダを操作できるわけではありません。

  • overrideMimeTypeを使ってレスポンスのMIMEタイプを書き換えることでXHRの処理を変えることができる場合があります。html なのに "text/plain" になってる場合とか

  • 次に、statusプロパティを紹介します。こちらは比較的よく使います。これは、帰ってきた結果のhttpステータスコードを数値で得ます

  • 400番台や500番台はページが見つからないなどの問題があったことを示すステータスコードですが、xhr的にはサーバーから何かしら結果が帰ってきたならば「通信成功」とみなされる

  • statusTextプロパティは、ステータスコードに対応するテキストでの説明です。これは各ステータスコードに対して定義されている文字列で、例えば200なら"OK"とか、404なら"Not Found"

  • load以外にもいろんなイベントがあるよ
  • 失敗した場合は原因によって、abort(中断)またはtimeout(タイムアウト)またはerror(その他)イベントが発生します

  • loadendイベントにおいて成功か失敗か見分ける方法としてはstatusプロパティを使う方法があります。通信成功した場合は前述の通り何らかのステータスコードが入っていますが、失敗した場合はstatusの値は0になっています。statusはまだリクエストしていない場合も0なので注意しましょう。

  • ステータスコード204 No Content
  • readystatechangeイベントについてですが、これはXHRオブジェクトのreadyStateというプロパティと関連しています。これは「現在の状態」を表す数値で、以下に列挙します。

  • 0 (UNSENT) まだopenメソッドが呼ばれていない状態。

  • 1 (OPENED) openメソッドが呼ばれたがsendメソッドは呼ばれていない状態。または、sendメソッドが呼ばれたあとまだレスポンスを受信していない状態。
  • 2 (HEADER_RECEIVED) sendが呼ばれた後、HTTPヘッダを全て受信し終えた状態。この状態からstatusプロパティやgetResponseHeaderメソッドなどが利用可能。まだデータ本体は受信していない。
  • 3 (LOADING) 本文のデータを受信中である状態。
  • 4 (DONE) 受信完了したか、リクエストに失敗して終了した状態。
  • 実は昔はこのreadystatechangeイベントしか無かったため、例えばロード完了はloadイベントではなくreadystatechangeイベントを監視してreadyStateが4になったらロード完了だと判断していました。

  • 確かに
  • 例えば。。xhr.timeout=1000;。。としたならば1秒以内に処理を完了できないとタイムアウトで失敗するということです。デフォルトの値は0で、これは無制限(タイムアウトしない)という意味になります。

  • ところで、イベントですから、イベントオブジェクトが存在します。

  • 確かに。存在感ここまでで0
  • ProgressEventという種類のイベントオブジェクト targetプロパティがありますね。この場合、targetはそのXMLHttpRequestオブジェクトになります。

  • 一つ目はloadedです。これは数値で、すでに受信した本文のバイト数です。。。次はtotalです。これは、受信すべきデータサイズの全体です。

  • totalがすでに判明しているかどうかを表すプロパティとしてlengthComputableがあります。これがtrueならば

  • progressイベントは、最短で0.05秒に1回発生することになっています

  • progressイベントと、 ev.total, ev.loaded を使うと進捗表示ができる
  • progress要素という、HTML要素がある
  • https://developer.mozilla.org/ja/docs/Web/HTML/Element/progress
  • XMLHttpRequestUploadです。これはXHRオブジェクトとセットになっていて、uploadプロパティに入っています。

  • クライアントからアップロードする部分のイベントを発生させてくれるとのこと
  • アップロードの進捗ヒョょじもできるとのこと
  • オリジンはホスト名・ポート番号・プロトコルを合わせた概念であり、これが1つでも違う2つのサイトは違うオリジンであると見なされます

  • リクエストを受ける側がhttpレスポンスヘッダによりクロスオリジンリクエストの受け入れを表明する必要があります

  • Access-Control-Allow-Origin: http://example.com http://uhyohyo.net

  • 複数許可はスペース区切り
  • ドメインを列挙する代わりに「*」とすることもできます。

  • 実はXHRオブジェクトにはwithCredentialsというプロパティがあります。これはクロスオリジンのときに関係するプロパティで、falseまたはtrue、デフォルトはfalseです。

  • trueになると何が起こるかというと、CookieとかSSL証明書などの情報も一緒に送られるようになります。これらが必要な場合はwithCredentialsをtrueにしましょう。

  • ただし、注意するのは、このwithCredentialsがtrueの状態で、さらにAccess-Control-Allow-Originが"*"でない場合(ちゃんと列挙してある場合)には、さらに追加のHTTPヘッダが必要になります。 Access-Control-Allow-Credientials: true

  • 実はopenメソッドの第3引数をfalseにすると同期的なリクエストが行われます。

  • この引数は省略するとtrue扱いになるという珍しい引数です。

  • JSではそうなんだ?
  • 現在の仕様では普通のjavascript実行環境ではxhrを同期リクエストで利用することはできません。つまり、第3引数にfalseを指定するとエラーになります。。。こんな意味のない仕様がなぜ存在するかというと、ひとつは歴史的経緯、そしてもうひとつはworkerプロセス内で利用するため

  • 同期リクエストでは、send()が呼び出された時点でブロックされ、リクエストが完了すると戻ってきます。 つまり、send()の直後にresponseプロパティを参照して結果を得ることができます。通信失敗した場合はerrorイベント等の代わりに例外が発生します。

  • バイナリデータを送りたい場合、sendの引数にArrayBufferやBlobを渡すことができます HTMLDocumentやXMLのDocumentを渡すこともできます。そうなると、そのソースを送ってくれます。

  • sendには今回初登場のFormDataオブジェクトを渡すこともできるのです。FormDataをsendに渡すと、HTMLでフォームを送信した場合と同様の本文を生成してくれます。FormDataの第一引数にHTMLFormElementを渡すとその内容を持ったFormDataが得られます。

  • teで作るのは多々変なものを作ってくれるらしい
  • var data = new FormData(); data.append("foo","bar");

  • 第2引数はBlobになります(もちろん、Blobを継承しているFileでもいいということに注意しましょう)。そして、第3引数にファイル名を与えることも可能です。すなわちこんな感じです。

  • //変数blobには何かのBlobが入っている data.append("foo",blob,"file.txt");
  • ファイルも遅れるFromData
  • ちなみに、ずいぶん長い間XMLHttpRequestがHTTP通信を発行する唯一の方法でしたが(Server-sent Eventsを除けば)、最近は新世代のfetchというAPIが登場して取って代わられようとしています。

2021/12/06 21:32 42minで読破した。

310文字/min

十三章第二回 Server-Sent Events

https://uhyohyo.net/javascript/13_2.html

5200字

  • Server-Sent Events (SSE)

  • XHRは“生”のHTTP通信を行うことができます

  • 今回のSSEはXHRに比べてハイレベルなAPIです。SSEで行うことができるHTTP通信の形は決まっていますが、より抽象化された形で通信を扱うことができます SSEを使うとpush型のデータ通信が可能になります

  • push型というのは、「サーバーから能動的にデータを送ってくる」ということです。

  • XHRでは、クライアント発しかできない pollingとか
  • サーバ発の方がリアルタイム
  • sseでは、http通信の範疇でpush型の通信を実現するために次のような方法を用います サーバーはレスポンスを送信中の状態を維持します。そして、サーバーは送りたいデータがあるたびにその接続でデータを送ります。

  • これではクライアントからデータをこの接続で送ることはできないけどね
  • サーバーはtext/event-streamというMIMEタイプでレスポンスを返します。サーバーから送られてくるデータの1単位はイベントといいます。

  • data: foo が1イベント
  • data: foo data: bar
  • これは、「foo(改行)bar」という2行の内容からなる1つのイベントです。
  • 空行くぎり
  • 送られてくるイベントデータの一行一行をフィールドといいます

  • フィールド名:フィールドの中身

  • フィールド名がdataであった場合、そのフィールドは、イベントの内容を表します。

  • event: がイベントの中身
  • イベントの名前はデフォルトである「message」

  • EventSourceというオブジェクトを使います。 var stream= new EventSource("/path/to/source");

  • eventSource.readyState 0(connecting) 1(open) 2(closed)
  • 1がイベント受信可能
  • sseの場合、サーバーからイベントを受信するたびに1回イベントが発生します。イベント名には、eventフィールドを利用して設定されたイベント名がそのまま使用されます。

  • stream.addEventListener("message",function(e){});

  • EventSource は stream という名前の変数になりがちらしい
  • 今回のイベントオブジェクトはMessageEventといいます。イベントの中身は、このdataプロパティに文字列で入っています。他にはoriginというプロパティがあり、これはイベントの配信元のURL(EventSourceの第1引数に指定したURLと同じ)が入っています

  • EventSourceはサーバーから接続が切断された場合再接続を試みる 上のreadyStateの2 (CLOSED)というのは、もう再接続もできず終了してしまったという状態

  • open や error イベントを活用しよう
  • error イベント (再接続しようとする時のイベント)で close する
  • retry:1000。data:hello
  • ミリ秒指定
  • idフィールドでイベントにidをつけられるぞ
  • 再接続時にサーバに教えると続きから送信できるように作れて便利
  • 以上でserver-sent eventsの説明は終了です。まとめると、httpでサーバー側から一方的なpush型の通信が可能だということです。あまり使い所がないかもしれませんが、もし機会があったら使うのもよいでしょう。

  • あまり使わないらしい

2021/12/07 23:57 17m

十三章第三回 Web Messaging

https://uhyohyo.net/javascript/13_3.html

4200文字 どんどん少なくなってく

  • 今回のこれはhttp通信はしません ブラウジングコンテキスト間の通信

  • ブラウザのタブ同士の通信とのこと
  • 自分で作ったブラウジングコンテキストなら知っている

  • window.openメソッドの第一引数はurlです。第二引数はウィンドウの名前です

  • 今回のサンプルはブロックされないブラウザが多いとおもいますが、これは「ボタンをクリックする」というユーザーの動作がトリガーになったせいでしょう。

  • このwindow.openの戻り値はwindowオブジェクトです。このwindowオブジェクトが、その開かれたウィンドウのブラウジングコンテキストを表すオブジェクトです

  • 今開いているページのブラウジングコンテキストに対応するのがおなじみのwindowです 開いたタブについては、そのページに対応するwindowオブジェクトを得ることができる

  • また、今まで慣れ親しんできたdocumentもwindowのプロパティです。以前述べたようにwindowのプロパティはグローバル変数として利用可能

  • 外から与えるのは「指示」だけで、実際の動作は内部で行われるようなのがいいでしょう。 操作する側のページに長々とどう変更するかのコードを送らなくてよいので

  • window.postMessage()
  • 送ることができるメッセージは、プリミティブ(文字列とか数値とか真偽値とか)や、普通のオブジェクト、配列、Dateオブジェクトなどです。また、最近よく出てくるFile, Blob, FileList, ArrayBufferも送ることができます。

  • 第2引数は送る先のオリジンです。これは、意図しないサイトに向けて情報を漏らしてしまわないように、メッセージ送る先のオリジンを指定できる この第2引数は省略できません 現在と同じ"/" 何でもOK "*"

  • メッセージを送りつけるだけなら、同一オリジンポリシーに縛られずに送ることができる

  • window.addEventListener("message",function(e){ });

  • MessageEvent
  • e.data にメッセージの中身が入ってる
  • sourceプロパティがあり、これはメッセージの送信元のブラウジングコンテキストに対応するwindowオブジェクトが入っています これを使えばメッセージを送ってきたページに対してメッセージを送り返すことができます

2021/12/08 0:12 31m

十三章第四回 Web Messaging 2

https://uhyohyo.net/javascript/13_4.html

  • 2021/12/08 22:10 0m
  • window オブジェクトで他のタブとやりとりしてると、複数の通信先タブがあると混在すん問題ッ解決する
  • そこで、通信相手ごとに専用の回線を用意して、その中で通信することができます。これがチャンネルです MessageChannel

  • チャンネルメッセージングにおいては、windowオブジェクトではなくMessagePortにおいてmessageイベントが発生します。 あるMessagePortでpostMessageメソッドを利用すると、それで発せられたメッセージはもう1つのMessagePortに届き、messageイベントが発生します。

  • MessagePortは、startとcloseというメソッド(いずれも引数無し)を持ちます

  • 普通のpostMessageでMessagePortを送って、あとはそのMessagePortで通信してね ここで、window.postMessageの第3引数が登場します。これは当然ながら省略可能であり、指定する場合は配列を渡します。配列であるという点に注意しましょう。
  • この配列の中にMessagePortを入れてやれば、向こうに届きます。受け取る側では、これはメッセージではありませんからイベントオブジェクトのdataプロパティには入っていません。window.postMessageの第3引数に渡されたものは、イベントオブジェクトのportsプロパティとして配列のまま届きます。

  • portやり取り用引数
  • なぜMessagePortは第1引数で普通に受け渡せないかというと、第1引数のデータは全てクローン(コピー)して送られるのに対し、MessagePortではクローンではなく現物をそのまま送らなければならない、という理屈からだそうです。第3引数は、現物を送るための機能ということになっていますが、今はMessagePort専用です。第3引数に渡したオブジェクトは向こうに送られてしまったので、それ以降こちら側で使うことはできません。

  • 他のオリジンのページと通信したいときに役に立つかもしれません。

読み終 2021/12/08 22:21 10m  2700文字だった 確かに 300 chars / min

感想

  • XHR以外にも2種類の通信方式を学んだ
  • 1分間で300字読んでることがわかった

時間まとめ

  • 2021/12/6-8
  • 46 + 32 + 10 + 10(ブログ) = 98分
  • 累計: 1441分(24.0時間)