JavaScript의 비동기식 취소 가능 함수
21342 단어 promiseasyncjavascript
async
함수에 대한 중복 호출을 처리하기 위해 생성기를 사용하는 방법을 설명합니다. Check out this gist for the final approach 또는 더 많은 것을 알기 위해 계속 읽으십시오!🎓)자바스크립트는 무서운 비동기 호출로 이루어진 굴곡의 미로로, 모든 호출은 같다.우리는 모두 이런 코드를 쓴 적이 있지만, 이 문장에서 나는
async
과 await
을 토론할 것이다.이 키워드는 widely supported으로 코드를 더 읽을 수 있는 곳으로 옮길 수 있습니다.📖👀가장 중요한 것은, 나는 관건적인 함정을 소개할 것이다. 어떻게 비동기적인 방법이 여러 번 실행되고, 이렇게 하면 다른 업무에 영향을 주지 않을 것이다.🏑💥
우리 이 예부터 시작합시다.이 함수는 몇 가지 내용을 가져와 화면에 표시하고 몇 초를 기다린 다음 주의하십시오.
function fetchAndFlash(page) {
const jsonPromise = fetch('/api/info?p=' + page)
.then((response) => response.json());
jsonPromise.then((json) => {
infoNode.innerHTML = json.html;
setTimeout(() => {
flashForAttention(infoNode);
}, 5000);
});
}
이제 async
과 await
으로 다시 쓸 수 있습니다. 리셋이 없습니다.async function fetchAndFlash(page) {
const response = await fetch('/api/info?p=' + page);
const json = await response.json();
infoNode.innerHTML = json.html;
// a bit awkward, but you can make this a helper method
await new Promise((resolve) => setTimeout(resolve, 5000));
flashForAttention(infoNode);
}
이게 낫지 않아요?이것은 이리저리 뛰어다니며 위에서 아래로 내려가는 절차를 쉽게 볼 수 있다. 자원을 얻고 JSON으로 변환하여 페이지에 쓰고 5초를 기다린 후에 다른 방법을 호출한다.🔜이건 함정이야!
그러나 이곳에는 독자들을 곤혹스럽게 할 수도 있다.이것은'일회성'으로 실행되는 일반적인 함수가 아니다.
await
을 호출할 때마다 우리는 기본적으로 브라우저의 이벤트 순환을 따른다. 이렇게 하면 계속 작업을 할 수 있다.⚡🤖fetchAndFlash()
을 사용하는 코드를 읽고 있다고 가정하십시오.만약 당신이 이 글의 제목을 읽지 않았다면, 만약 당신이 이 코드를 실행한다면, 당신은 무슨 일이 발생하기를 기대합니까?fetchAndFlash('page1');
fetchAndFlash('page2');
너는 하나하나 발생하기를 기대할 수도 있고, 혹은 하나하나가 다른 것을 취소할 수도 있다.그러나 사실은 그렇지 않습니다. 둘 다 병행 실행될 것입니다. (대기할 때 JavaScript가 막을 수 없기 때문에) 임의의 순서로 완성되고 HTML이 최종적으로 페이지에 나타날지 알 수 없습니다.⚠️분명히 이런 방법은 리셋된 버전을 바탕으로 하는 것도 완전히 같은 문제가 있지만, 매우 혐오스러운 방식으로 더욱 뚜렷하다.코드를
async
과 await
으로 현대화할 때 우리는 그것을 더욱 모호하게 한다.😕우리들은 몇 가지 다른 방법을 소개하여 이 문제를 해결합시다.안전벨트 매!🎢
메서드 #1: 체인
async
방법을 호출하는 방식과 원인에 따라 하나씩 링크할 수 있습니다.클릭 이벤트를 처리하는 경우:let p = Promise.resolve(true);
loadButton.onclick = () => {
const pageToLoad = pageToLoadInput.value;
// wait for previous task to finish before doing more work
p = p.then(() => fetchAndFlash(pageToLoad));
};
클릭할 때마다 체인에 다른 작업이 추가됩니다.우리는 또한 보조 함수로 이 점을 요약할 수 있다.// makes any function a chainable function
function makeChainable(fn) {
let p = Promise.resolve(true);
return (...args) => {
p = p.then(() => fn(...args));
return p;
};
}
const fetchAndFlashChain = makeChainable(fetchAndFlash);
현재, 당신은 fetchAndFlashChain()
으로 전화를 걸기만 하면 됩니다. 그것은 다른 fetchAndFlashChain()
으로 전화를 걸면 순서대로 발생합니다.🔗그러나 이것은 본문의 건의가 아닙니다. 만약 우리가 이전의 조작을 취소하고 싶다면 어떻게 합니까?사용자가 방금 다른 불러오기 단추를 눌렀기 때문에 이전의 일에 관심이 없을 수도 있습니다.🙅
방법 #2: 장애물 검사
우리의 현대화된
fetchAndFlash()
에서 우리는 await
키워드를 세 번 사용했는데 실제로는 두 가지 다른 원인만 있다.우리는 a nonce으로 각각의 조작을 표시함으로써 이 점을 실현할 수 있다.이것은 로컬 및 글로벌에 이 객체를 저장하고 다른 작업이 로컬 버전에서 시작되었기 때문에 글로벌 버전이 서로 다른지 확인하는 유일한 객체를 만드는 것을 의미합니다.
다음은 저희가 업데이트한
fetchAndFlash()
방법입니다.let globalFetchAndFlashNonce;
async function fetchAndFlash(page) {
const localNonce = globalFetchAndFlashNonce = new Object();
const response = await fetch('/api/info?p=' + page);
const json = await response.json();
// IMMEDIATELY check
if (localNonce !== globalFetchAndFlashNonce) { return; }
infoNode.innerHTML = json.html;
await new Promise((resolve) => setTimeout(resolve, 5000));
// IMMEDIATELY check
if (localNonce !== globalFetchAndFlashNonce) { return; }
flashForAttention(infoNode);
}
이것은 매우 좋지만, 좀 지나치다.요약하기도 쉽지 않으니 중요한 곳에 검사를 추가하는 것을 기억해야 한다!비록 generators을 사용하여 우리를 개괄하는 방법이 있다.
배경: 발전기
우리의 예에서,
await
은 기다리는 일이 끝날 때까지 실행을 늦추고, 네트워크 요청이나 시간 초과만 기다린다. 생성기 함수는 기본적으로 상반된 일을 하고, 그 위치를 되돌려 사용한다.곤혹스러웠어특히
function* myGenerator() {
const finalOut = 300;
yield 1;
yield 20;
yield finalOut;
}
for (const x of myGenerator()) {
console.info(x);
}
// or, slightly longer (but exactly the same output)
const iterator = myGenerator();
for (;;) {
const next = iterator.next();
if (next.done) {
break;
}
console.info(next.value);
}
이 두 버전의 프로그램은 모두 1, 20, 300을 인쇄할 것이다.흥미로운 것은 내가 for
순환에서 좋아하는 일을 할 수 있다는 것이다. break
초기, myGenerator
의 모든 상태가 변하지 않고, 내가 성명한 모든 변수, 그리고 내가 어디에 있는지를 포함한다.여기는 보이지 않지만 생성기를 호출하는 코드 (특히 교체기의
.next()
함수) 도 변수를 사용하여 복구할 수 있습니다.우리는 기대하고 있다.만약 우리가 어떤 작업을 멈추기로 결정한다면, 우리는 이 부분들을 결합시켜서 더 이상 일을 하지 않거나, 출력을 사용하여 실행을 회복할 수 있다.응, 우리 문제에 잘 어울리는 것 같아!✅
솔루션🎉
마지막으로
fetchAndFlash()
을 다시 쓰겠습니다.실제로 우리는 함수 유형 자체를 변경하고 await
과 yield
을 교환하기만 하면 된다. 호출자는 우리를 기다릴 수 있고 다음 단계는 어떻게 조작하는지 볼 수 있다.function* fetchAndFlash(page) {
const response = yield fetch('/api/info?p=' + page);
const json = yield response.json();
infoNode.innerHTML = json.html;
yield new Promise((resolve) => setTimeout(resolve, 5000));
flashForAttention(infoNode);
}
이 코드는 지금 정말 의미가 없습니다. 만약 우리가 그것을 사용하려고 시도한다면, 그것은 붕괴될 것입니다.모든 Promise
을 생성하는 요점은 현재 이 생성기를 호출하는 함수들이 nonce 검사를 포함하여 await
을 실행할 수 있다는 것이다.현재 yield
을 사용하면 기다릴 때 이 줄을 삽입할 필요가 없습니다.가장 중요한 것은 이 방법이 현재
async
함수가 아니라 생성기이기 때문에 await
키워드는 사실상 오류이다.이것은 당신이 정확한 코드를 작성하는 것을 확보하는 가장 좋은 방법입니다!🚨우리는 어떤 기능을 필요로 합니까?그래, 이것이 이 글의 진정한 매력이다.
function makeSingle(generator) {
let globalNonce;
return async function(...args) {
const localNonce = globalNonce = new Object();
const iter = generator(...args);
let resumeValue;
for (;;) {
const n = iter.next(resumeValue);
if (n.done) {
return n.value; // final return value of passed generator
}
// whatever the generator yielded, _now_ run await on it
resumeValue = await n.value;
if (localNonce !== globalNonce) {
return; // a new call was made
}
// next loop, we give resumeValue back to the generator
}
};
}
신기하지만 그것도 의미가 있었으면 좋겠어요.우리는 전달된 생성기를 호출하여 교체기를 얻었다.그리고 우리는 생성된 모든 값에 await
을 실행하고 생성기가 완성될 때까지 네트워크 응답처럼 생성된 값을 복구합니다.중요한 것은 우리가 매번 비동기적인 조작을 한 후에 전체 국면과 일부 nonce를 검사할 수 있게 하는 것이다.분기: 새 호출이 진행되면 하나의 호출이 취소되었는지 확인하는 것이 유용하기 때문에 특수한 값을 되돌려줍니다.sample gist에서 나는
Symbol
, 그것과 비교할 수 있는 유일한 대상으로 돌아왔다.마지막으로, 우리는 실제로
makeSingle
을 사용하고 우리의 생성기를 다른 사람이 사용할 수 있도록 포장했기 때문에 현재의 작업 원리는 일반적인 비동기적인 방법과 유사하다.// replaces fetchAndFlash so all callers use it as an async method
fetchAndFlash = makeSingle(fetchAndFlash);
// ... later, call it
loadButton.onclick = () => {
const pageToLoad = pageToLoadInput.value;
fetchAndFlash(pageToLoad); // will cancel previous work
};
대단히 좋다현재, 당신은 어느 곳에서든 fetchAndFlash()
으로 전화를 걸 수 있으며, 이전의 모든 통화는 가능한 한 빨리 취소될 것이라는 것을 알고 있습니다.방백: 추출 중단 가능
열성적인 사람들은 내가 위에서 소개한 것은 단지 한 가지 방법을 취소했을 뿐, 어떤 비행 중의 일도 중단하지 않았다는 것을 알아차릴 수 있을 것이다.내가 말한 것은
fetch
이다. 그것은 지원되는 방식 to abort the network request을 가지고 있다.만약 비동기적인 기능이 매우 큰 파일을 다운로드한다면, 이것은 사용자의 대역폭을 절약할 수 있을 것이다. 그러나 우리가 하는 것은 다운로드를 막지 않을 것이다. 우리는 파일이 귀중한 바이트를 다 쓴 후에 다운로드를 취소할 뿐이다.도은
만약 당신이 여기까지 읽었다면, 자바스크립트의 작업 방식에 대해 더 많은 생각을 할 수 있기를 바랍니다.
비동기적인 작업을 해야 할 때 JS는 막을 수 없습니다. 방법에 대한 여러 번의 호출이 발생할 수 있습니다. 이 링크를 처리하거나 본고의 전체 주제에서 말한 바와 같이 이전의 호출을 취소할 수 있습니다.
읽어주셔서 감사합니다!👋
Reference
이 문제에 관하여(JavaScript의 비동기식 취소 가능 함수), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/chromiumdev/cancellable-async-functions-in-javascript-5gp7텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)