Re: [whatwg/fetch] Aborting a fetch: The Next Generation (#447)

The following solution enables cancellation of Fetch requests in all browsers, including IE. It would also enable targeted cancellation of any other javascript execution (e.g. creating a blob with javascript code and abort any of that code execution on demand).

Firefox:
![image](https://user-images.githubusercontent.com/8843669/28247756-61995584-6a37-11e7-8560-492e5b47ca89.png)

Chrome:
![image](https://user-images.githubusercontent.com/8843669/28247758-75e6a474-6a37-11e7-9a68-27cf28e36e57.png)

```javascript
var fetch = (function() {
    var fetchcount = 0;

    // Create IE + others compatible event handler
    var handlers = window.addEventListener ? ["addEventListener", "removeEventListener"] : ["attachEvent", "detachEvent"];
    var watchEvent = handlers[0] == "attachEvent" ? "onmessage" : "message";
    var watch = window[handlers[0]];
    var unwatch = window[handlers[1]];

    var cancelableFetch = function() {

        var cancel; // cancel method
        var fetchArgs = Array.prototype.slice.call(arguments);

        var promise = new Promise(function(resolve, reject) {

            var cancelled = false;
            var fetchid = ++fetchcount;

            var iframe = document.createElement('iframe');
            iframe.id = iframe.name = 'fetch' + fetchid;
            iframe.style = 'display:none;';
            document.body.appendChild(iframe);

            var d = (iframe.contentWindow || iframe.contentDocument);
            if (d.document) {
                d = d.document
            }

            var resolveFetch = function(e) {
                if (e.data[0] === fetchid) {
                    resolve(e.data[1]);
                    unwatch(watchEvent, resolveFetch, false);
                }
            };

            // Listen to message from child window
            watch(watchEvent, resolveFetch, false);

            d.open();
            d.write('<script>fetch.apply(this,'+JSON.stringify(fetchArgs)+').then(function(response) { response.text().then(function(data) { parent.postMessage([' + fetchid + ',data],' + JSON.stringify(document.location.href) + '); }); });</script>');
            d.close();

            cancel = function() {
                if (cancelled) {
                    return;
                }
                cancelled = true;
                console.warn('Fetch aborted');

                try {

                    if (typeof(window.frames['fetch' + fetchid].stop) === 'undefined') {
                        //Internet Explorer code
                        window.frames['fetch' + fetchid].document.execCommand('Stop');
                    } else {
                        //Other browsers
                        window.frames['fetch' + fetchid].stop();
                    }
                } catch (e) {}

                document.body.removeChild(iframe);
            };

        });
        return {
            then: function(resolve) {
                return promise.then(resolve);
            },
            cancel: cancel
        };
    };
    return cancelableFetch;
})();

// reqular fetch request
var request = fetch('https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js');

// regular processing of result
request.then(function(body) {
    console.log("Fetch complete:", (typeof body === 'string') ? body.length : body);
}).catch(function(err) {
    console.log(err.message);
});

// cancel request after +/- 170ms (fine tune to test cancellation in Firefox)
setTimeout(function() {
    request.cancel();
}, 170);
```

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/whatwg/fetch/issues/447#issuecomment-315608008

Received on Sunday, 16 July 2017 13:04:49 UTC