Edited at

そろそろ XHR を Fetch で置き換えれるようになってきた

More than 1 year has passed since last update.

みなさんの記憶には、 Fetch ではリクエストのキャンセルや progress が取れなく、 XHR を置き換えることはできないといったイメージがあるかと思います。

ですが、ブラウザは常に進化しています。既にリクエストもキャンセルできますし、 progress も取得可能です(対応ブラウザに限る)。

以下で紹介します。


リクエストのキャンセル

fetch() メソッドには第2引数に signal を渡せます。この signal と AbortController を使うことでリクエストのキャンセルが実現できるのです。

const controller = new AbortController();

const signal = controller.signal;

setTimeout(() => controller.abort(), 10);

fetch('http://www.example.com/', {signal})
.then(res => {
return res.text();
})
.then(text => {
console.log(text);
})
.catch(e => {
if (e.name === 'AbortError') {
console.error('The user aborted a request.');
} else {
console.error(e);
}
});


progress

これは XHR#onprogress を使っていた処理のことです。

Fetch では Streams API の ReadableStream を使って実現します。

fetch('<URL>')

.then(res => {
const total = parseInt(res.headers.get('content-length'), 10);
let loaded = 0;

return new Response(
new ReadableStream({
start(controller) {
const reader = res.body.getReader();

async function read() {
let result = await reader.read();
while (!result.done) {
const value = result.value;
loaded += value.byteLength;
console.log(`${loaded} / ${total}`);
controller.enqueue(value);
result = await reader.read();
}
controller.close();
return;
};
read();
}
})
);
})
.then(res => res.blob())
.then(data => console.log('download completed'))
.catch(e => console.error(e));

progress.png