HTTP fetch()リクエストをキャンセルするにはどうすればよいですか?


回答:


281

TL / DR:

fetchsignal2017年9月20日の時点でパラメータをサポートしていますが、現時点ではすべてのブラウザでサポートされているわけではありません

2020年の更新:ほとんどの主要ブラウザー(Edge、Firefox、Chrome、Safari、Opera、およびその他いくつか)は、DOM生活標準の一部となったこの機能をサポートしています。(2020年3月5日現在)

これは、間もなく発生する変更ですので、AbortControllers を使用してリクエストをキャンセルできますAbortSignal

ロングバージョン

方法:

それが機能する方法はこれです:

ステップ1:あなたはAbortController(今のところこれを使用しました)を作成します

const controller = new AbortController()

ステップ2:次のAbortControllerようなs信号を取得します。

const signal = controller.signal

ステップ3signalを取得するために次のように渡します:

fetch(urlToFetch, {
    method: 'get',
    signal: signal, // <------ This is our AbortSignal
})

ステップ4:必要なときはいつでも中止する:

controller.abort();

これがどのように機能するかの例を示します(Firefox 57以降で機能します)。

<script>
    // Create an instance.
    const controller = new AbortController()
    const signal = controller.signal

    /*
    // Register a listenr.
    signal.addEventListener("abort", () => {
        console.log("aborted!")
    })
    */


    function beginFetching() {
        console.log('Now fetching');
        var urlToFetch = "https://httpbin.org/delay/3";

        fetch(urlToFetch, {
                method: 'get',
                signal: signal,
            })
            .then(function(response) {
                console.log(`Fetch complete. (Not aborted)`);
            }).catch(function(err) {
                console.error(` Err: ${err}`);
            });
    }


    function abortFetching() {
        console.log('Now aborting');
        // Abort.
        controller.abort()
    }

</script>



<h1>Example of fetch abort</h1>
<hr>
<button onclick="beginFetching();">
    Begin
</button>
<button onclick="abortFetching();">
    Abort
</button>

出典:

  • AbortControllerの最終バージョンがDOM仕様に追加されました
  • 対応するPRフェッチ仕様については現在マージされます。
  • AbortControllerの実装を追跡するブラウザのバグは、こちらから入手できます:Firefox:#1378342、Chromium:#750599、WebKit:#174980、Edge:#13009916

2
この答えは正解であり、賛成投票する必要があります。しかし、実際にはFirefox 57以降では実際には機能していなかったため、コードスニペットを自由に編集しました。shimが原因で失敗したようです(「Err:TypeError:RequestInitの 'signal'メンバーインターフェースAbortSignalを実装していません。」)、slowwly.robertomurray.co.ukの証明書に問題があるようです「このサーバーは、slowwly.robertomurray.co.ukであることを証明できません。セキュリティ証明書は*からのものです。 .herokuapp.com。”)なので、slowwly.robertomurray.co.uk(プレーンhttp)を使用するように変更しました。
sideshowbarker 2017年

3
ただし、他のブラウザ、つまりChromeでは動作しませんAbortController is not defined。とにかく、これは概念の証明にすぎません。少なくともFirefox 57以降を使用している人は、それが機能していることを確認できます
SudoPlz

3
これは純粋なStackOverflowゴールドです。簡潔な説明をありがとうございます。そしてバグトラッカーのリンクも!
Kjellski

3
現在、すべての最新のブラウザでサポートされています。developer.mozilla.org/en-US/docs/Web/API/AbortController/abort下の表を参照
Alex Ivasyuv

2
おかげで、私はまだ質問があります、次のフェッチのために信号を手動でtrueに戻す必要がありますか?
akshay kishore

20

https://developers.google.com/web/updates/2017/09/abortable-fetch

https://dom.spec.whatwg.org/#aborting-ongoing-activities

// setup AbortController
const controller = new AbortController();
// signal to pass to fetch
const signal = controller.signal;

// fetch as usual
fetch(url, { signal }).then(response => {
  ...
}).catch(e => {
  // catch the abort if you like
  if (e.name === 'AbortError') {
    ...
  }
});

// when you want to abort
controller.abort();

エッジ16(2017-10-17)、firefox 57(2017-11-14)、デスクトップサファリ11.1(2018-03-29)、iOSサファリ11.4(2018-03-29)、クロム67(2018-05)で動作します-29)以降。


古いブラウザでは、githubのwhatwg-fetch polyfillAbortController polyfillを使用できます。古いブラウザを検出し、条件付きポリフィルを使用することもできます。

import 'abortcontroller-polyfill/dist/abortcontroller-polyfill-only'
import {fetch} from 'whatwg-fetch'

// use native browser implementation if it supports aborting
const abortableFetch = ('signal' in new Request('')) ? window.fetch : fetch

:githubののを使用している場合は、これはそれを行うことが可能である、ちょうど彼らのREADMEの指示に従い、ポリフィルをフェッチgithub.com/github/fetch#aborting-requests
ファビオ・サントス

@FábioSantosあなたのコメントは質問にあるべきですか、それともそれに対する答えとしてでしょうか?それは私の答えに固有のものではありません。
Jayen

github fetch polyfillを使用している人のためのメモです。AFAIKは入手可能な最も人気のあるフェッチポリフィルであり、使用している関数であるフェッチにポリフィルを提供するため、これはあなたの回答に関連していると思いました。古いブラウザのため、多くの人がこのポリフィルを使用します。人々はポリフィルがすべてを修正することを前提としているので、言及することが重要であることがわかりましたが、この特定のものはAbortControllerをポリフィルしようとしません。彼らは古いブラウザでポリフィルされると考えてAbortControllerを使おうとします、そしてブーム、例外的なケースには例外があり、古いブラウザでのみです。
ファビオサントス

5

2018年2月現在、fetch()Chromeで以下のコードを使用してキャンセルできます(Readable Streams使用して Firefoxサポートを有効にするをお読みください)。catch()ピックアップしてもエラーは発生せず、これAbortControllerが完全に採用されるまでの一時的な解決策です。

fetch('YOUR_CUSTOM_URL')
.then(response => {
  if (!response.body) {
    console.warn("ReadableStream is not yet supported in this browser.  See https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream")
    return response;
  }

  // get reference to ReadableStream so we can cancel/abort this fetch request.
  const responseReader = response.body.getReader();
  startAbortSimulation(responseReader);

  // Return a new Response object that implements a custom reader.
  return new Response(new ReadableStream(new ReadableStreamConfig(responseReader)));
})
.then(response => response.blob())
.then(data => console.log('Download ended. Bytes downloaded:', data.size))
.catch(error => console.error('Error during fetch()', error))


// Here's an example of how to abort request once fetch() starts
function startAbortSimulation(responseReader) {
  // abort fetch() after 50ms
  setTimeout(function() {
    console.log('aborting fetch()...');
    responseReader.cancel()
    .then(function() {
      console.log('fetch() aborted');
    })
  },50)
}


// ReadableStream constructor requires custom implementation of start() method
function ReadableStreamConfig(reader) {
  return {
    start(controller) {
      read();
      function read() {
        reader.read().then(({done,value}) => {
          if (done) {
            controller.close();
            return;
          }
          controller.enqueue(value);
          read();
        })
      }
    }
  }
}

2
これはOPが求めていたものではありません。リーダーではなくフェッチをキャンセルしたいのです。Fetchのpromiseは、リクエストが完了するまで解決されません。サーバーへのリクエストをキャンセルするには遅すぎます。
ラーリー、

3

@sproが言うように、今のところ適切な解決策はありません。

ただし、処理中の応答があり、ReadableStreamを使用している場合は、ストリームを閉じて要求をキャンセルできます。

fetch('http://example.com').then((res) => {
  const reader = res.body.getReader();

  /*
   * Your code for reading streams goes here
   */

  // To abort/cancel HTTP request...
  reader.cancel();
});

0

ポリフィルしましょう:

if(!AbortController){
  class AbortController {
    constructor() {
      this.aborted = false;
      this.signal = this.signal.bind(this);
    }
    signal(abortFn, scope) {
      if (this.aborted) {
        abortFn.apply(scope, { name: 'AbortError' });
        this.aborted = false;
      } else {
        this.abortFn = abortFn.bind(scope);
      }
    }
    abort() {
      if (this.abortFn) {
        this.abortFn({ reason: 'canceled' });
        this.aborted = false;
      } else {
        this.aborted = true;
      }
    }
  }

  const originalFetch = window.fetch;

  const customFetch = (url, options) => {
    const { signal } = options || {};

    return new Promise((resolve, reject) => {
      if (signal) {
        signal(reject, this);
      }
      originalFetch(url, options)
        .then(resolve)
        .catch(reject);
    });
  };

  window.fetch = customFetch;
}

コードはテストされていないことを覚えておいてください!あなたがそれをテストし、何かがうまくいかなかったら私に知らせてください。JavaScript公式ライブラリから「フェッチ」関数を上書きしようとする警告が表示される場合があります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.