Generator와 Promise의 형태를 잘 이해하고 있어야 풀이가 가능한 문제이다.
주요 포인트는 모든 Generator 절차를 수행하기 이전에 취소 요청이 들어온다면 비동기 작업이 취소될 수 있도록 할 수 있는가 이다.
function cancellable<T>(generator: Generator<Promise<any>, T, unknown>): [() => void, Promise<T>] {
let isCancelled = false;
let cancelResolve: (() => void) | null = null;
// 취소 프로미스 생성
const cancelPromise = new Promise<never>((_, reject) => {
cancelResolve = () => reject("Cancelled");
});
// 제너레이터를 실행하고 결과를 처리하는 함수
const run = async (): Promise<T> => {
let next = generator.next();
while (!next.done) {
try {
// Promise.race를 사용하여 취소와 현재 작업 중 먼저 완료되는 것 처리
const result = await Promise.race([next.value, cancelPromise]);
next = generator.next(result);
} catch (error) {
// 에러 발생 시 제너레이터에 전달
if (error === "Cancelled") {
isCancelled = true;
}
next = generator.throw(error);
}
}
// 제너레이터 완료 시 최종 값 반환
return next.value;
};
// 취소 함수
const cancel = () => {
if (!isCancelled && cancelResolve) {
cancelResolve();
}
};
// Promise 생성 및 반환
const promise = run().catch((error) => {
if (error === "Cancelled") {
return Promise.reject(error);
}
throw error;
});
return [cancel, promise];
}