(2021/08/29 追記)
既に fetch を使っても問題なくなっているので XMLHttpRequest の代わりにそちらを利用することをおすすめします。
この記事について
XMLHttpRequest(以下、XHR)のイベントの仕様って、そういえばきちんと把握してないなと思ったので少し調べてみました
その結果をつらつらと書いていきます
通信の終了/成功or失敗の調べ方
本題のイベントに入る前におさらいです
後々必要になります
readyState
xhr.readyStateは以下のいずれかの値を持ちます
| 値 | 定数 | 状態 |
|---|---|---|
| 0 | UNSENT | XHRオブジェクトの作成直後 |
| 1 | OPENED |
open()メソッドの呼び出し後 |
| 2 | HEADERS_RECEIVED | レスポンスヘッダの受信後 |
| 3 | LOADING | レスポンスボディを受信中(繰り返し実行される) |
| 4 | DONE | XHR通信の完了後 |
readyStateが4なら通信終了です
status
レスポンスのHTTPステータスです
readyStateが2以上の時アクセス可能で、4になった時に最終的な値が確定します
0なら通信に失敗、0以外なら通信に成功したことを表します
ただし、statusが0以外であってもサーバーへのリクエストが成功したとは限りません(404, 500など)
XHRのイベント
XHRが通信中に起こすイベントは以下の8個
onreadystatechangeonloadstartonprogressonloadendonloadonerroronabortontimeout
onreadystatechange
もっとも古くから存在する(?)イベントです
readyStateの値が更新されるたびに呼び出されます
onloadstart
open()メソッドが呼ばれたタイミングで呼び出されます
つまり、下の二つは同じです
xhr.onloadstart = function() {
// do something
}
xhr.onreadystatechange = function() {
if(xhr.readyState === 1) {
// do something
}
}
onprogress
レスポンスボディの受信中に繰り返し呼び出されます
つまり、下の二つは同じです
xhr.progress = function() {
// do something
}
xhr.onreadystatechange = function() {
if(xhr.readyState === 3) {
// do something
}
}
onloadend
受信が終了した時に呼び出されます
onloadendは受信の成功or失敗に関わらず呼び出されるので、後述するonloadとonerrorを合わせたものだとも言えます
つまり、以下の3つは同じです
xhr.onloadend = function() {
// do something
}
xhr.onload = function() {
// do something
}
xhr.onerror = function() {
// do something
}
xhr.onreadystatechange = function() {
if(xhr.readyState === 4) {
// do something
}
}
onload
受信が成功した時に呼び出されます
つまり、以下の2つは同じです
xhr.onload = function() {
// do something
}
xhr.onreadystatechange = function() {
if(xhr.readyState === 4 && xhr.status !== 0) {
// do something
}
}
onerror
受信が失敗した時に呼び出されます
つまり、以下の2つは同じです
xhr.onerror = function() {
// do something
}
xhr.onreadystatechange = function() {
if(xhr.readyState === 4 && xhr.status === 0) {
// do something
}
}
onabort
通信がabort()メソッドによって中断された場合に呼び出されます
これはonreadystatechangeでは代用できません
ontimeout
接続がタイムアウトした場合に呼び出されます
一応、setTimeoutやらを使えばreadystatechangeで代用は可能ですが割愛します
その他
以下の4つのイベントは同時に発火することがありません
- onload
- onerror
- onabort
- ontimeout
よって、4つ全て指定しないと通信の終了を取りこぼす可能性があります
ちなみに、それぞれのイベントでのreadyStateとstatusは以下のようになります
| イベント | readyState | status |
|---|---|---|
| onload | 4 | 0以外(レスポンスによる) |
| onerror | 4 | 0 |
| onabort | 通信の進行度による | 0 |
| ontimeout | 通信の進行度による | 0 |
そもそも、onerrorってどんなときに発火するのかについてですが、例えばXHRのCross-Origin制約に引っかかった場合やネットワーク接続ができない場合など、サーバーとの接続ができなかった場合にerrorとなるようです
まとめ
通信の終了を検知したい場合は, 上記の4つのイベントを見ていれば大丈夫なようです
プログレスバーを実装したい場合は、onprogressを使ってイベントオブジェクトから総バイト数と受信済みバイト数が取得可能なようなので、それで事足りそうです