[소소한 개발 일지] popstate 이벤트에서 앞으로가기, 뒤로가기 구분하기
비동기 요청 취소하기, 그리고 AbortController
2020-03-14
Explanation
한 달에 적어도 한두 개씩은 글 써야지 생각하는데 이미 2월엔 글을 못썼네요.. 요즘 회사 일이 많아서..(는 핑계)
요즘 코로나 때문에 다들 고생이 이만저만이 아닌 거 같아요. 다들 건강 조심하시고, 확진 받으신 분들은 빨리 쾌차하시길 바랄게요.
오늘은 비동기 요청을 취소하는 것에 대해?? 적어보려고 합니다.
음.. XMLHttpRequest는 사실 별로 적을게 없어요. 그냥 abort() 메서드를 지원한답니다.
1 2 3 4 5 6 7 8 9 10 |
var xhr = new XMLHttpRequest(), method = "GET", url = "https://developer.mozilla.org/"; xhr.open(method, url, true); xhr.send(); if (OH_NOES_WE_NEED_TO_CANCEL_RIGHT_NOW_OR_ELSE) { xhr.abort(); } |
예제는 깔끔하게 모질라 형 누나들(MDN)의 예제 https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/abort
(절대 귀찮아서 예제 복붙한건 안비밀..)
음.. 사실 jQuery도 별로 적을게 없어요. 버전에 따라 응답 형식이 다르지만, 모든 버전에서 abort() 메서드를 지원합니다.
1 2 3 4 5 6 7 8 9 10 11 |
var xhr = $.ajax({ type: "POST", url: "some.php", data: "name=John&location=Boston", success: function(msg){ alert( "Data Saved: " + msg ); } }); //kill the request xhr.abort(); |
이번엔 우리의 스택오버플로 형 누나들의 예제. 아래 링크에 보시면 형 누나들 답변들 보면 조금 더 자세한 정보도 알 수 있답니다.
https://stackoverflow.com/questions/446594/abort-ajax-requests-using-jquery
사실 여기부터 본론이랍니다.
Fetch는 Promise를 반환하죠. 그런데 Promise는 abort() 메서드가 없답니다. 그리고 TC39에서 Promise Cancelable에 대하여 제안이 있었지만 철회되었답니다.
참고. https://github.com/tc39/proposal-cancelable-promises
약간 주제 외 이야기 일 수도 있지만,
저는 예전에 안희종님의 ECMAScript와 TC39에 대해 적어주신 글을 읽은 적이 있는데, 엄청 좋아서 추천해드려요! 저는 엄청 흥미롭게 읽었어요.
https://ahnheejong.name/articles/ecmascript-tc39/
비동기 통신할때 Axios 라이브러리도 많이 사용하잖아요? Axios는 Promise 기반이지만 Cancel을 지원한답니다. 설명을 읽어보면, 앞서 이야기한 TC39의 취소된 Promise Cancelable 제안을 기초로 하여 제공한다고 해요. 사용은 해봤지만, 어떤 구조로 되어있는진 잘 모르겠어요.(소스를 까보진 않아서…)
1 2 3 4 5 6 7 8 9 10 11 12 |
const CancelToken = axios.CancelToken; let cancel; axios.get('/user/12345', { cancelToken: new CancelToken(function executor(c) { // An executor function receives a cancel function as a parameter cancel = c; }) }); // cancel the request cancel(); |
역시 예제는 공식 깃헙예제.
https://github.com/axios/axios#cancellation
Fetch는 Promise기반이고 Promise 객체는 resolve, reject 메서드를 지원하니까 reject를 써서 대충, 대략 구현하려고 하면 아래와 같이 할 수 있을 거 같아요.
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 |
const promise = () => { let rejectAction; const promiseFnc = new Promise((resolve, reject) => { rejectAction = reject; setTimeout(() => { resolve('ok'); }, 5000); }).catch(e => { console.log('cancel request'); }); return {rejectAction, promiseFnc}; }; const test = async () => { const { rejectAction, promiseFnc } = promise(); setTimeout(() => { rejectAction(); console.log('cancel action'); }, 1000); const result = await promiseFnc; console.log(result); }; test(); // cancel action // cancel request |
역시 예제코드 만들기도 쉬운일이 아닙니다..
그리고 이제는 AbortController를 사용해서 Promise의 abort를 구현할 수 있답니다.
참고. https://dom.spec.whatwg.org/#interface-abortcontroller
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
// abort in 1 second let controller = new AbortController(); setTimeout(() => controller.abort(), 1000); try { let response = await fetch('/article/fetch-abort/demo/hang', { signal: controller.signal }); } catch(err) { if (err.name == 'AbortError') { // handle abort() alert("Aborted!"); } else { throw err; } } |
위 예제는 https://ko.javascript.info/fetch-abort에서 가져왔습니다. 자세한, 더 많은 예제는 링크를 통해 살펴보시면 좋을 것 같아요.
저는 AbortController를 엄청 최근에서야 알게 되었는데요.(비동기 요청을 취소하는 것에 대해서 생각한 것도 사실 얼마되지 않..) AbortController는 DOM에 정의되어 있는 Interface에요. 예전에 구글 형 누나들에게 검색했을땐 이 정보를 찾지 못했는데, 최근에서야 알게 된게 뭔가 이상해서 한번 DOM 깃헙에 가서 언제 정의 되었는지 찾아봤어요.
https://github.com/whatwg/dom/commit/1c58d2cf4279ade63f1c5c7d200156cfdda89b7c
찾아보니 2017년 7월 14일에 정의되었네요. 헐…
아마도 2017년에 정의되었는데, MDN에서 올해 3월에 깔끔히 정리되서 기재되고 그 후에 검색 엔진에 잡혀서 제가 최근에 알게 되었던 거 같아요.
2017년이면 좀 예전이기도 하지만, 또 어떻게 생각하면 비교적 최근 스팩이기 때문에 fetch 보다도 호환되는 브라우저가 적어요. 사용하신다면 함께 확인해보시는게 좋을 거 같아요.
https://caniuse.com/#search=abortController
물론, 이미 폴리필도 있어요.
https://www.npmjs.com/package/abortcontroller-polyfill