기술나눔

프런트 엔드에서 인터페이스 호출을 취소하는 방법

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

🧑‍💻 写在开头
点赞 + 收藏 === 学会🤣🤣🤣

1. xmlHttpRequest는 어떻게 요청을 취소합니까?

인스턴스화된 XMLHttpRequest 객체에는 중단 메서드도 있습니다.

const xhr = new XMLHttpRequest();
xhr.addEventListener('load', function(e) {
  console.log(this.responseText);
});
xhr.open('GET', 'https://jsonplaceholder.typicode.com/todos/1');
xhr.send();

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
// 返回
{
  "userId": 1,
  "id": 1,
  "title": "delectus aut autem",
  "completed": false
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

전송 직후 중단된 경우 취소

// xhr的取消操作:执行过程比较模糊,不知道abort什么时机进行处理
xhr.abort()

  • 1
  • 2
  • 3

타이머 도중(타이머 지속 시간이 인터페이스 요청 지속 시간과 유사한 경우) 요청을 취소하면 리소스를 획득한 것으로 표시되지만 콘솔에는 인쇄되지 않습니다.
여기에 이미지 설명을 삽입하세요.

2. 중단 컨트롤러

const ac = new AbortController();
const { signal } = ac;
const url = "https://jsonplaceholder.typicode.com/todos/1";
​
fetch(url, { signal })
  .then((res) => res.json())
  .then((json) => console.log(json));

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

요청을 취소하려면 직접 중단을 사용하세요.

ac.abort()

  • 1
  • 2

여기에 이미지 설명을 삽입하세요.
여기에 보고된 오류의 이유는 오류가 캡처되지 않았기 때문입니다.

// 修改后的代码
fetch(url, { signal: ac.signal })
  .then((res) => res.json())
  .then((json) => console.log(json))
  .catch(e => console.log(e)) // DOMException: signal is aborted without reason
ac.abort() // abort接受一个入参,会被传递到signal的reason属性中

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

왜 이렇게 취소가 가능한가요?

fetch는 신호 객체의 상태를 모니터링하고 요청을 종료할 수 있습니다.

2.1 동시에 여러 요청을 취소하는 방법은 무엇입니까?

const ac = new AbortController();
const { signal } = ac;
const url = "https://jsonplaceholder.typicode.com/todos";
​
const todoRequest = (id, { signal }) => {
  fetch(`${url}/${id}`, { signal })
    .then((res) => res.json())
    .then((json) => console.log(json))
    .catch((e) => console.log(e)); // DOMException: signal is aborted without reason
};
​
todoRequest(1, { signal });
todoRequest(2, { signal });
todoRequest(3, { signal });
​
ac.abort("cancled");

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

여기에 이미지 설명을 삽입하세요.

2.2 중단 신호

작업이 완료되기 전에 중단될 수 있도록 수행 중인 비동기 작업과 통신할 수 있도록 하는 신호 개체를 나타내는 데 사용되는 인터페이스입니다.

2.3 AbortSignal 메소드

2.3.1 중단

중단된 AbortSignal 개체를 만드는 데 사용되는 정적 메서드입니다. 이 메서드를 호출하면 중단 상태가 true인 AbortSignal 인스턴스가 반환됩니다.

const signalAbout = AbortSignal.abort(); // AbortSignal {aborted: true, reason: DOMException: signal is aborted without reason...}

  • 1
  • 2

2.3.2 throwIfAborted 메소드

코드를 실행하기 전에 AbortSignal이 중단되었는지 확인하는 데 사용됩니다. AbortSignal이 중단된 경우 AbortError가 발생합니다. 이 방법은 개발자가 불필요한 처리를 피하기 위해 특정 작업을 수행하기 전에 중단되지 않았는지 확인하는 데 도움이 될 수 있습니다.

const signalAbout = AbortSignal.abort('abortedReason');
try {
    signalAbout.throwIfAborted(); // 抛出error: abortedReason
} catch (error) {
    console.log(error);
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

2.3.3 타임아웃

지정된 시간 후에 자동으로 종료되는 AbortSignal 개체를 만드는 데 사용됩니다. 이는 요청 시간 제한을 설정해야 할 때 유용합니다.

// 使用 AbortSignal.timeout 设置 10ms超时
const signalAbout = AbortSignal.timeout(10);
const todoRequest = (id, { signal }) => {
  fetch(`${url}/${id}`, { signal })
    .then((res) => res.json())
    .then((json) => console.log("json: ", json))
    .catch((e) => console.log("err: ", e)); //DOMException: signal timed out 
};
todoRequest(1, { signal: signalAbout });

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

여기에 이미지 설명을 삽입하세요.

2.3.3.1 이벤트 리스너 추가 => 종료되지 않음에서 종료됨으로

AbortSignal은 중단 이벤트를 수신하는 데 사용되며 EventTarget은 이벤트 리스너를 추가, 제거 및 트리거하는 메커니즘을 제공하므로 EventTarget에서 상속됩니다.

const signalAbout = AbortSignal.timeout(10);
signalAbout.addEventListener("abort", (e) => {
    console.log("aborted: ", e);
})
​

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

e의 인쇄는 다음과 같습니다.
여기에 이미지 설명을 삽입하세요.

3. 사전에 취소된 약속을 이행합니다.

const ac = new AbortController();
const { signal } = ac;
​
const cancelablePromise = ({signal}) => 
    new Promise((resolve, reject) => {
        // 情况1:直接主动取消
        signal?.throwIfAborted(); // 也可以用reject
​
        // 情况2:正常处理业务逻辑
​
        setTimeout(() => {
            Math.random() > 0.5 ? resolve('data') : reject('fetch error');
        }, 1000);
​
        // 情况3:超时 todo?
​
        // 监听取消
        signal.addEventListener("abort", () => {
            reject(signal?.reason);
        });
    })
// 发起网络请求
cancelablePromise({signal})
.then(res => console.log('res: ', res))
.catch(err => console.log('err: ', err))
// 情况1 
// ac.abort('用户离开页面了') // err:  用户离开页面了
​
// 情况2 正常请求 err:  fetch error || res:  data

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

4. 신호를 사용하여 이벤트 청취를 취소하는 방법은 무엇입니까?

하나의 요소에 여러 개의 이벤트 리스너를 추가하는 경우에는 RemoveEventListener처럼 취소할 필요가 없으며, 각 이벤트는 한 번만 취소해야 하며, 매번 해당 이벤트의 이벤트 핸들을 작성해야 합니다.

신호를 사용할 때 신호를 한 번만 취소하면 모든 이벤트 모니터링이 취소됩니다.

const ac = new AbortController();
const { signal } = ac;
const eleA = document.querySelector('#a');
const eleB = document.querySelector('#b');
​
function aEleHandler () {}; // 事件
eleA.addEventListener('click', aEleHandler, {signal}); // 无论绑定多少个事件,都只需要一个signal
​
eleB.addEventListener('click', () => {
    ac.abort(); // 只需要取消一次
})

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

5. 데이터 조립을 위해 여러 인터페이스가 요청되는 시나리오

네트워크 속도가 좋지 않을 때 지속적인 네트워크 요청을 취소하는 방법은 무엇입니까?

const ac = new AbortController();
const { signal } = ac;
const fetchAndRenderAction = (signal) => {
    requestData(signal); // 多个串行或者并行的接口
    drawAndRender(signal); // 异步渲染
}
​
try{
    fetchAndRenderAction({signal})
}catch{
    // dosomething...
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

6. 요약

사용자가 적극적으로 페이지를 떠나거나 사용자의 네트워크가 매우 정체된 경우(예상 반환 순서는 인터페이스 1 => 인터페이스 2이지만 인터페이스 1의 반환이 너무 느려서 순서가 혼란스러워집니다.) 이를 위해서는 수동이 필요합니다. 요청 종료. 생성자 AbortController의 인스턴스 세마포 신호(ref로 저장 가능), 신호는 가져오기의 매개변수로 사용됩니다. 각 요청에서 중단 메소드를 수동으로 호출하여 이전 요청을 취소할 수 있습니다.

도움이 되셨다면 관심을 가져주시길 바랍니다. 기술 문서를 정기적으로 업데이트하여 모두가 함께 토론하고 배우고 발전할 수 있도록 하겠습니다.
여기에 이미지 설명을 삽입하세요.