Here's our solution which falls back to XHR, while trying to stay close to fetch's signature. Note, that this focuses on the progress, response status and response data are omitted.
interface OnProgress {
(loaded: number, total: number, done: boolean): void;
}
export function fetch(url: string, { method, headers, body }, onProgress: OnProgress): Promise {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open(method, url, true);
xhr.upload.addEventListener('progress', e => onProgress(e.loaded, e.total, false));
xhr.upload.addEventListener('loadend', _ => onProgress(undefined, undefined, true));
xhr.onreadystatechange = () => {
if (xhr.readyState === 4)
resolve();
};
xhr.onerror = err => reject(err);
Object.entries(headers).forEach(([name, value]) => xhr.setRequestHeader(name, value));
xhr.send(body);
});
}
Usage example:
import { fetch } from 'my-fetch-progress';
const formData = new FormData();
formData.append('file', file, file.name);
const onProgress = (loaded, total, done) => console.log('onProgress', loaded, total, done);
fetch('/my-upload', {
method: 'POST',
headers: { 'Authorization': `Bearer ...` },
body: formData
}, onProgress)
.then(_ => /*...*/);