◆ Promise が多い
◆ text メソッドでテキストで取得できる
◆ body の ReadableStreamReader は使い方が微妙
◆ デフォルトだと cookie が送られない 

今日は Promise 調べてたのも実は fetch で使われてたからだったりします

fetch

fetch は xhr をもっと楽に使えるもの
fetch("/test.html")
これだけでその URL のデータが取得できます

fetch 関数の返り値は Promise です
どんなデータが取れるのか見てみます

Response

var res = null
fetch("/").then(e => res = e)

res
// Response {}
Response 型のデータが取得できました

プロパティは
res.url
// http://var.blog.jp/

res.type
// basic

res.status
// 200

res.statusText
// OK

res.ok
// true

などがあります

Headers

headers にヘッダーが入っています
res.headers
// Headers {}

Headers 型はプロパティにデータはないようです
メソッドを使って
res.headers.forEach((value, key) => console.log(key, ": ", value))
// date : Sat, 16 Jan 2016 14:26:55 GMT
// content-encoding : gzip
// vary : User-Agent,Accept-Encoding
// server : Plack::Handler::Starlet
// p3p : CP="BUS OUR PHY STP ADM CUR DEV PSA PSD"
// transfer-encoding : chunked
// content-type : text/html; charset=utf-8
// connection : close
// x-framework : JP/4.01
こうするようです

他にも keys, values, entries でイテレータ取得ができます
あとは get, set, has, delete, append があります

set や append があるのでヘッダーですし リクエストの時にも使うみたいです

Promise で読みだす系

他には text, json, blob というメソッドがあります
res.text
// function text() { [native code] }

res.json
// function json() { [native code] }

res.blob
// function blob() { [native code] }

どれも実行すると Promise が返って来ます
then で受け取ったデータが各種データです

blob の場合

var blob = res.blob()
// Promise {[[PromiseStatus]]: "resolved", [[PromiseValue]]: Blob}

blob.then(e => console.log(e))
// Blob {}
// size: 76779
// type: "text/html"

text の場合


一番良く使うと思うので fetch から全体
fetch("/").then(e => e.text()).then(e => console.log(e.substr(0, 10)))
// <!doctype

json の場合

fetch("/")
.then(e => e.json())
.then(e => console.log(e.substr(0, 10)))
.catch(e => console.log("parse error"))
// parse error

catch を忘れずに

ReadableByteStream

body というプロパティがあったので試してみます
res.body
// ReadableByteStream {}

ReadableByteStream というものらしいです
cancel と getReader メソッドしかないです

getReader メソッドを実行してみると
res.body.getReader()
// ReadableByteStreamReader {}
// closed: Promise
// cancel: function cancel()
// read: function read()
// releaseLock: function releaseLock()

ReadableByteStreamReader という型になりました
長い……

ReadableByteStreamReader

reader と来たらもう read メソッドを使うしかないです
res.body.getReader().read()
// Promise {[[PromiseStatus]]: "resolved", [[PromiseValue]]: Object}

res.body.getReader().read().then(e => console.log(e))
// Object {}
// done: false
// value: Uint8Array[2353]
Promise が返って来ます
データを取り出すと Uint8Array
バイナリデータで取得してるようです
でも 2353 バイトって少なくないような

done が false なのでまだ続きそうです

さらによくみたらこのオブジェクトのフォーマットってイテレータの next メソッドの返り値です
でも next なんてメソッドはないですし 続きの取得方法がわかりません

とりあえず reader の read を複数回やってみる
fetch("/").then(e => e.body.getReader()).then(reader => {
reader.read().then(e => console.log(e))
reader.read().then(e => console.log(e))
reader.read().then(e => console.log(e))
})
// Object {done: false, value: Uint8Array[2353]}
一回しか表示されてないです

REPL でやると毎回データが変わっていたので setTimeout で時間差実行してみます
fetch("/").then(e => e.body.getReader()).then(reader => {
reader.read().then(e => console.log(e))
setTimeout(() => reader.read().then(e => console.log(e)), 1000)
})
// Object {done: false, value: Uint8Array[2563]}
// Object {done: false, value: Uint8Array[4347]}
別のデータが取得できてます

ということは
fetch("/").then(e => e.body.getReader()).then(reader => {
read()
function read(){
reader.read().then(e => {
console.log(e)
e.done || setTimeout(read, 300)
})
}
})
// Object {done: false, value: Uint8Array[2353]}
// Object {done: false, value: Uint8Array[5943]}
// Object {done: false, value: Uint8Array[30151]}
// Object {done: false, value: Uint8Array[23667]}
// Object {done: false, value: Uint8Array[5594]}
// Object {done: false, value: Uint8Array[9071]}
// Object {done: true, value: undefined}
done が true になるまで繰り返して全部取れました

でも setTimeout って絶対やり方間違ってると思います
何かいい方法があるはず
読み取り可能になったらみたいなものがきっと

Node.js だと on で readable とかイベントつけておけばできたと思うのですが メソッド一覧を見てもないので その方法が使えないみたいです

諦めてぐぐってみたのですが
Streams Standard
それっぽいものはありましたが 書かれてるものが未実装だったりこの仕様自体が未確定なものが多いです
特に ReadableByteStreamReader なんかほとんど TBA です

TBA は To Be Announced の略でまだ決まってないから後で ということだそうです

エラーの場合

ところで エラーの場合は status などのプロパティはこうなりました
fetch("/ffffff").then(e => console.log(e))
// Response {}
// body: (...)
// bodyUsed: false
// headers: Headers {}
// ok: false
// status: 404
// statusText: "Not Found"
// type: "basic"
// url: "http://var.blog.jp/ffffff"

cookie 送れない

普通にテキスト取り出しする分には問題なく使えますが cookie が送信されていないようです
そのせいでログイン必要なページを開くと ログイン画面の HTML やエラー JSON が返って来ます

cookie のセットもできるとは思いますが ググッて簡単に見当たらなかったのでまたいつか調べます
xhr みたいにデフォルトはそのユーザの cookie 送ってくれればいいのに