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

3 단계 : 다음 signal과 같이 페치를 전달합니다 .

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>

출처 :


2
이 답변은 정확하며 상향 조정되어야합니다. 그러나 코드 스 니펫은 Firefox 57 이상에서 실제로 작동하지 않았기 때문에 코드 조각을 자유롭게 편집 할 수있었습니다. shim이 실패하게 만드는 것처럼 보였습니다 ( “Err : TypeError : 'signal'Member of RequestInit 인터페이스 AbortSignal을 구현하지 않습니다.” ) 및 slowwly.robertomurray.co.uk 의 인증서에 문제가있는 것 같습니다 ( “이 서버는 slowwly.robertomurray.co.uk임을 증명할 수 없습니다. ) . 나는 slowwly.robertomurray.co.uk (일반 http)를 사용하도록 변경했습니다 .
sideshowbarker 1

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), 파이어 폭스 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ábio Santos

@ FábioSantos 귀하의 의견은 질문에 대한 답입니까, 아니면 그 자체의 답입니까? 그것은 내 대답에만 국한되지 않습니다.
Jayen

github fetch polyfill을 사용하는 사람들에게 참고하십시오. AFAIK는 가장 인기있는 페치 폴리 필이며 사용중인 함수 인 페치를 폴리 필하기 때문에 귀하의 답변과 관련이 있다고 생각했습니다. 오래된 브라우저로 인해 많은 사람들이이 폴리 필을 사용하게됩니다. 사람들이 polyfill이 모든 것을 고쳤다 고 가정하기 때문에 언급하는 것이 중요하다는 것을 알았습니다. 그러나이 특정 것은 AbortController를 polyfill하지 않습니다. 그들은 AbortController를 사용하려고 시도했습니다. 오래된 브라우저에서는 폴리 채워질 것이라고 생각하고 붐이 발생합니다.
Fábio Santos

5

2018 년 2 월 현재 fetch()Chrome에서 아래 코드를 사용하여 취소 할 수 있습니다 ( Firefox 지원을 사용하려면 Readable Streams 사용 참조). 오류가 발생하지 않으며 완전히 채택 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의 약속은 요청이 완료된 후 서버에 대한 요청을 취소하기에는 너무 늦을 때까지 해결되지 않습니다.
Rahly

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 공식 라이브러리에서 'fetch'함수를 덮어 쓰려고한다는 경고가 표시 될 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.